/* Copyright (C) Paul Falstad and Iain Sharp This file is part of CircuitJS1. CircuitJS1 is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. CircuitJS1 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with CircuitJS1. If not, see <http://www.gnu.org/licenses/>. */ package com.lushprojects.circuitjs1.client; // GWT conversion (c) 2015 by Iain Sharp // For information about the theory behind this, see Electronic Circuit & System Simulation Methods by Pillage import java.util.Vector; import java.util.HashMap; import java.util.Map; import java.util.Random; import java.lang.Math; import com.google.gwt.canvas.client.Canvas; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.CellPanel; import com.google.gwt.user.client.ui.DockLayoutPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.RootLayoutPanel; import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.canvas.dom.client.Context2d; import com.google.gwt.canvas.dom.client.Context2d.LineCap; import com.google.gwt.event.dom.client.MouseDownEvent; import com.google.gwt.event.dom.client.MouseDownHandler; import com.google.gwt.event.dom.client.MouseEvent; import com.google.gwt.event.dom.client.MouseMoveEvent; import com.google.gwt.event.dom.client.MouseMoveHandler; import com.google.gwt.event.dom.client.MouseUpHandler; import com.google.gwt.event.dom.client.MouseUpEvent; import com.google.gwt.event.dom.client.MouseOutEvent; import com.google.gwt.event.dom.client.MouseOutHandler; import com.google.gwt.event.dom.client.ContextMenuEvent; import com.google.gwt.event.dom.client.ContextMenuHandler; import com.google.gwt.user.client.Event.NativePreviewEvent; import com.google.gwt.user.client.Event.NativePreviewHandler; import com.google.gwt.event.dom.client.MouseWheelEvent; import com.google.gwt.event.dom.client.MouseWheelHandler; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.http.client.Request; import com.google.gwt.http.client.RequestException; import com.google.gwt.http.client.Response; import com.google.gwt.http.client.RequestBuilder; import com.google.gwt.http.client.RequestCallback; import com.google.gwt.user.client.ui.MenuBar; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.DoubleClickHandler; import com.google.gwt.event.dom.client.DoubleClickEvent; import com.google.gwt.dom.client.CanvasElement; import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.user.client.ui.MenuItem; import com.google.gwt.safehtml.shared.SafeHtml; import com.google.gwt.safehtml.shared.SafeHtmlUtils; import com.google.gwt.storage.client.Storage; import com.google.gwt.user.client.ui.PopupPanel; import static com.google.gwt.event.dom.client.KeyCodes.*; import com.google.gwt.http.client.URL; import com.google.gwt.user.client.Window.Location; import com.google.gwt.user.client.ui.Frame; import com.google.gwt.user.client.ui.Widget; import com.google.gwt.user.client.Window.Navigator; public class CirSim implements MouseDownHandler, MouseMoveHandler, MouseUpHandler, ClickHandler, DoubleClickHandler, ContextMenuHandler, NativePreviewHandler, MouseOutHandler, MouseWheelHandler { Random random; public static final int sourceRadius = 7; public static final double freqMult = 3.14159265*2*4; // IES - remove interaction Button resetButton; Button runStopButton; Button dumpMatrixButton; MenuItem aboutItem; MenuItem importFromLocalFileItem, importFromTextItem, exportAsUrlItem, exportAsLocalFileItem, exportAsTextItem; MenuItem importFromDropboxItem; MenuItem exportToDropboxItem; MenuItem undoItem, redoItem, cutItem, copyItem, pasteItem, selectAllItem, optionsItem; MenuBar optionsMenuBar; CheckboxMenuItem dotsCheckItem; CheckboxMenuItem voltsCheckItem; CheckboxMenuItem powerCheckItem; CheckboxMenuItem smallGridCheckItem; CheckboxMenuItem crossHairCheckItem; CheckboxMenuItem showValuesCheckItem; CheckboxMenuItem conductanceCheckItem; CheckboxMenuItem euroResistorCheckItem; CheckboxMenuItem printableCheckItem; CheckboxMenuItem conventionCheckItem; private Label powerLabel; private Label titleLabel; private Scrollbar speedBar; private Scrollbar currentBar; private Scrollbar powerBar; MenuBar elmMenuBar; MenuItem elmEditMenuItem; MenuItem elmCutMenuItem; MenuItem elmCopyMenuItem; MenuItem elmDeleteMenuItem; MenuItem elmScopeMenuItem; MenuItem elmFlipMenuItem; MenuBar scopeMenuBar; MenuBar transScopeMenuBar; MenuBar mainMenuBar; CheckboxMenuItem scopeVMenuItem; CheckboxMenuItem scopeIMenuItem; CheckboxMenuItem scopeScaleMenuItem; CheckboxMenuItem scopeMaxMenuItem; CheckboxMenuItem scopeMinMenuItem; CheckboxMenuItem scopeFreqMenuItem; CheckboxMenuItem scopeFFTMenuItem; CheckboxMenuItem scopeRMSMenuItem; CheckboxMenuItem scopePowerMenuItem; CheckboxMenuItem scopeIbMenuItem; CheckboxMenuItem scopeIcMenuItem; CheckboxMenuItem scopeIeMenuItem; CheckboxMenuItem scopeVbeMenuItem; CheckboxMenuItem scopeVbcMenuItem; CheckboxMenuItem scopeVceMenuItem; CheckboxMenuItem scopeVIMenuItem; CheckboxMenuItem scopeXYMenuItem; CheckboxMenuItem scopeResistMenuItem; CheckboxMenuItem scopeVceIcMenuItem; CheckboxMenuItem scopeMaxScaleMenuItem; CheckboxMenuItem scopeMaxScaleTransMenuItem; MenuItem scopeRemovePlotMenuItem; MenuItem scopeSelectYMenuItem; static HashMap<String,String> localizationMap; String lastCursorStyle; boolean mouseWasOverSplitter = false; // Class addingClass; PopupPanel contextPanel = null; int mouseMode = MODE_SELECT; int tempMouseMode = MODE_SELECT; String mouseModeStr = "Select"; static final double pi = 3.14159265358979323846; static final int MODE_ADD_ELM = 0; static final int MODE_DRAG_ALL = 1; static final int MODE_DRAG_ROW = 2; static final int MODE_DRAG_COLUMN = 3; static final int MODE_DRAG_SELECTED = 4; static final int MODE_DRAG_POST = 5; static final int MODE_SELECT = 6; static final int MODE_DRAG_SPLITTER = 7; static final int infoWidth = 120; long myframes =1; long mytime=0; long myruntime=0; long mydrawtime=0; int dragGridX, dragGridY, dragScreenX, dragScreenY, initDragGridX, initDragGridY; long mouseDownTime; long zoomTime; int mouseCursorX = -1; int mouseCursorY = -1; int selectedSource; Rectangle selectedArea; int gridSize, gridMask, gridRound; boolean dragging; boolean analyzeFlag; boolean dumpMatrix; // boolean useBufferedImage; boolean isMac; String ctrlMetaKey; double t; int pause = 10; int scopeSelected = -1; int menuScope = -1; int menuPlot = -1; int hintType = -1, hintItem1, hintItem2; String stopMessage; double timeStep; static final int HINT_LC = 1; static final int HINT_RC = 2; static final int HINT_3DB_C = 3; static final int HINT_TWINT = 4; static final int HINT_3DB_L = 5; Vector<CircuitElm> elmList; // Vector setupList; CircuitElm dragElm, menuElm, stopElm; private CircuitElm mouseElm=null; boolean didSwitch = false; int mousePost = -1; CircuitElm plotXElm, plotYElm; int draggingPost; SwitchElm heldSwitchElm; double circuitMatrix[][], circuitRightSide[], origRightSide[], origMatrix[][]; RowInfo circuitRowInfo[]; int circuitPermute[]; boolean simRunning; boolean circuitNonLinear; int voltageSourceCount; int circuitMatrixSize, circuitMatrixFullSize; boolean circuitNeedsMap; // public boolean useFrame; int scopeCount; Scope scopes[]; boolean showResistanceInVoltageSources; int scopeColCount[]; static EditDialog editDialog, customLogicEditDialog; static ExportAsUrlDialog exportAsUrlDialog; static ExportAsTextDialog exportAsTextDialog; static ExportAsLocalFileDialog exportAsLocalFileDialog; static ImportFromTextDialog importFromTextDialog; static ImportFromDropbox importFromDropbox; static ExportToDropbox exportToDropbox; static ScrollValuePopup scrollValuePopup; static AboutBox aboutBox; static ImportFromDropboxDialog importFromDropboxDialog; // Class dumpTypes[], shortcuts[]; String shortcuts[]; static String muString = "\u03bc"; static String ohmString = "\u03a9"; String clipboard; Rectangle circuitArea; Vector<String> undoStack, redoStack; double transform[]; DockLayoutPanel layoutPanel; MenuBar menuBar; MenuBar fileMenuBar; VerticalPanel verticalPanel; CellPanel buttonPanel; private boolean mouseDragging; double scopeHeightFraction=0.2; Vector<CheckboxMenuItem> mainMenuItems = new Vector<CheckboxMenuItem>(); Vector<String> mainMenuItemNames = new Vector<String>(); LoadFile loadFileInput; Frame iFrame; Canvas cv; Context2d cvcontext; Canvas backcv; Context2d backcontext; static final int MENUBARHEIGHT=30; static int VERTICALPANELWIDTH=166; // default static final int POSTGRABSQ=25; static final int MINPOSTGRABSIZE = 256; final Timer timer = new Timer() { public void run() { updateCircuit(); } }; final int FASTTIMER=16; int getrand(int x) { int q = random.nextInt(); if (q < 0) q = -q; return q % x; } public void setCanvasSize(){ int width, height; width=(int)RootLayoutPanel.get().getOffsetWidth(); height=(int)RootLayoutPanel.get().getOffsetHeight(); height=height-MENUBARHEIGHT; width=width-VERTICALPANELWIDTH; if (cv != null) { cv.setWidth(width + "PX"); cv.setHeight(height + "PX"); cv.setCoordinateSpaceWidth(width); cv.setCoordinateSpaceHeight(height); } if (backcv != null) { backcv.setWidth(width + "PX"); backcv.setHeight(height + "PX"); backcv.setCoordinateSpaceWidth(width); backcv.setCoordinateSpaceHeight(height); } setCircuitArea(); } void setCircuitArea() { int height = cv.getCanvasElement().getHeight(); int width = cv.getCanvasElement().getWidth(); int h = (int) ((double)height * scopeHeightFraction); /*if (h < 128 && winSize.height > 300) h = 128;*/ circuitArea = new Rectangle(0, 0, width, height-h); } // Circuit applet; CirSim() { // super("Circuit Simulator v1.6d"); // applet = a; // useFrame = false; theSim = this; } String startCircuit = null; String startLabel = null; String startCircuitText = null; String startCircuitLink = null; // String baseURL = "http://www.falstad.com/circuit/"; public void init() { boolean printable = false; boolean convention = true; boolean euroRes = false; boolean usRes = false; MenuBar m; CircuitElm.initClass(this); QueryParameters qp = new QueryParameters(); try { //baseURL = applet.getDocumentBase().getFile(); // look for circuit embedded in URL // String doc = applet.getDocumentBase().toString(); String cct=qp.getValue("cct"); if (cct!=null) startCircuitText = cct.replace("%24", "$"); startCircuit = qp.getValue("startCircuit"); startLabel = qp.getValue("startLabel"); startCircuitLink = qp.getValue("startCircuitLink"); euroRes = qp.getBooleanValue("euroResistors", false); usRes = qp.getBooleanValue("usResistors", false); printable = qp.getBooleanValue("whiteBackground", getOptionFromStorage("whiteBackground", false)); convention = qp.getBooleanValue("conventionalCurrent", getOptionFromStorage("conventionalCurrent", true)); } catch (Exception e) { } boolean euroSetting = false; if (euroRes) euroSetting = true; else if (usRes) euroSetting = false; else euroSetting = getOptionFromStorage("euroResistors", !weAreInUS()); transform = new double[6]; String os = Navigator.getPlatform(); isMac = (os.toLowerCase().contains("mac")); ctrlMetaKey = (isMac) ? "Cmd" : "Ctrl"; shortcuts = new String[127]; layoutPanel = new DockLayoutPanel(Unit.PX); fileMenuBar = new MenuBar(true); importFromLocalFileItem = new MenuItem(LS("Import From Local File"), new MyCommand("file","importfromlocalfile")); importFromLocalFileItem.setEnabled(LoadFile.isSupported()); fileMenuBar.addItem(importFromLocalFileItem); importFromTextItem = new MenuItem(LS("Import From Text"), new MyCommand("file","importfromtext")); fileMenuBar.addItem(importFromTextItem); importFromDropboxItem = new MenuItem(LS("Import From Dropbox"), new MyCommand("file", "importfromdropbox")); fileMenuBar.addItem(importFromDropboxItem); exportAsUrlItem = new MenuItem(LS("Export As Link"), new MyCommand("file","exportasurl")); fileMenuBar.addItem(exportAsUrlItem); exportAsLocalFileItem = new MenuItem(LS("Export As Local File"), new MyCommand("file","exportaslocalfile")); exportAsLocalFileItem.setEnabled(ExportAsLocalFileDialog.downloadIsSupported()); fileMenuBar.addItem(exportAsLocalFileItem); exportAsTextItem = new MenuItem(LS("Export As Text"), new MyCommand("file","exportastext")); fileMenuBar.addItem(exportAsTextItem); exportToDropboxItem = new MenuItem(LS("Export To Dropbox"), new MyCommand("file", "exporttodropbox")); exportToDropboxItem.setEnabled(ExportToDropbox.isSupported()); fileMenuBar.addItem(exportToDropboxItem); fileMenuBar.addSeparator(); aboutItem=new MenuItem(LS("About"),(Command)null); fileMenuBar.addItem(aboutItem); aboutItem.setScheduledCommand(new MyCommand("file","about")); int width=(int)RootLayoutPanel.get().getOffsetWidth(); VERTICALPANELWIDTH = width/5; if (VERTICALPANELWIDTH > 166) VERTICALPANELWIDTH = 166; if (VERTICALPANELWIDTH < 128) VERTICALPANELWIDTH = 128; menuBar = new MenuBar(); menuBar.addItem(LS("File"), fileMenuBar); verticalPanel=new VerticalPanel(); // make buttons side by side if there's room buttonPanel=(VERTICALPANELWIDTH == 166) ? new HorizontalPanel() : new VerticalPanel(); m = new MenuBar(true); m.addItem(undoItem = menuItemWithShortcut(LS("Undo"), LS("Ctrl-Z"), new MyCommand("edit","undo"))); m.addItem(redoItem = menuItemWithShortcut(LS("Redo"), LS("Ctrl-Y"), new MyCommand("edit","redo"))); m.addSeparator(); m.addItem(cutItem = menuItemWithShortcut(LS("Cut"), LS("Ctrl-X"), new MyCommand("edit","cut"))); m.addItem(copyItem = menuItemWithShortcut(LS("Copy"), LS("Ctrl-C"), new MyCommand("edit","copy"))); m.addItem(pasteItem = menuItemWithShortcut(LS("Paste"), LS("Ctrl-V"), new MyCommand("edit","paste"))); pasteItem.setEnabled(false); m.addItem(menuItemWithShortcut(LS("Duplicate"), LS("Ctrl-D"), new MyCommand("edit","duplicate"))); m.addSeparator(); m.addItem(selectAllItem = menuItemWithShortcut(LS("Select All"), LS("Ctrl-A"), new MyCommand("edit","selectAll"))); m.addSeparator(); m.addItem(new MenuItem(weAreInUS() ? LS("Center Circuit") : LS("Centre Circuit"), new MyCommand("edit", "centrecircuit"))); m.addItem(menuItemWithShortcut(LS("Zoom 100%"), "0", new MyCommand("edit", "zoom100"))); m.addItem(menuItemWithShortcut(LS("Zoom In"), "+", new MyCommand("edit", "zoomin"))); m.addItem(menuItemWithShortcut(LS("Zoom Out"), "-", new MyCommand("edit", "zoomout"))); menuBar.addItem(LS("Edit"),m); MenuBar drawMenuBar = new MenuBar(true); drawMenuBar.setAutoOpen(true); menuBar.addItem(LS("Draw"), drawMenuBar); m = new MenuBar(true); m.addItem(new MenuItem(LS("Stack All"), new MyCommand("scopes", "stackAll"))); m.addItem(new MenuItem(LS("Unstack All"),new MyCommand("scopes", "unstackAll"))); m.addItem(new MenuItem(LS("Combine All"),new MyCommand("scopes", "combineAll"))); menuBar.addItem(LS("Scopes"), m); optionsMenuBar = m = new MenuBar(true ); menuBar.addItem(LS("Options"), optionsMenuBar); m.addItem(dotsCheckItem = new CheckboxMenuItem(LS("Show Current"))); dotsCheckItem.setState(true); m.addItem(voltsCheckItem = new CheckboxMenuItem(LS("Show Voltage"), new Command() { public void execute(){ if (voltsCheckItem.getState()) powerCheckItem.setState(false); setPowerBarEnable(); } })); voltsCheckItem.setState(true); m.addItem(powerCheckItem = new CheckboxMenuItem(LS("Show Power"), new Command() { public void execute(){ if (powerCheckItem.getState()) voltsCheckItem.setState(false); setPowerBarEnable(); } })); m.addItem(showValuesCheckItem = new CheckboxMenuItem(LS("Show Values"))); showValuesCheckItem.setState(true); //m.add(conductanceCheckItem = getCheckItem(LS("Show Conductance"))); m.addItem(smallGridCheckItem = new CheckboxMenuItem(LS("Small Grid"), new Command() { public void execute(){ setGrid(); } })); m.addItem(crossHairCheckItem = new CheckboxMenuItem(LS("Show Cursor Cross Hairs"), new Command() { public void execute(){ setOptionInStorage("crossHair", crossHairCheckItem.getState()); } })); crossHairCheckItem.setState(getOptionFromStorage("crossHair", false)); m.addItem(euroResistorCheckItem = new CheckboxMenuItem(LS("European Resistors"), new Command() { public void execute(){ setOptionInStorage("euroResistors", euroResistorCheckItem.getState()); } })); euroResistorCheckItem.setState(euroSetting); m.addItem(printableCheckItem = new CheckboxMenuItem(LS("White Background"), new Command() { public void execute(){ int i; for (i=0;i<scopeCount;i++) scopes[i].setRect(scopes[i].rect); setOptionInStorage("whiteBackground", printableCheckItem.getState()); } })); printableCheckItem.setState(printable); m.addItem(conventionCheckItem = new CheckboxMenuItem(LS("Conventional Current Motion"), new Command() { public void execute(){ setOptionInStorage("conventionalCurrent", conventionCheckItem.getState()); } })); conventionCheckItem.setState(convention); m.addItem(optionsItem = new CheckboxAlignedMenuItem(LS("Other Options..."), new MyCommand("options","other"))); mainMenuBar = new MenuBar(true); mainMenuBar.setAutoOpen(true); composeMainMenu(mainMenuBar); composeMainMenu(drawMenuBar); layoutPanel.addNorth(menuBar, MENUBARHEIGHT); layoutPanel.addEast(verticalPanel, VERTICALPANELWIDTH); RootLayoutPanel.get().add(layoutPanel); cv =Canvas.createIfSupported(); if (cv==null) { RootPanel.get().add(new Label("Not working. You need a browser that supports the CANVAS element.")); return; } cvcontext=cv.getContext2d(); backcv=Canvas.createIfSupported(); backcontext=backcv.getContext2d(); setCanvasSize(); layoutPanel.add(cv); verticalPanel.add(buttonPanel); buttonPanel.add(resetButton = new Button(LS("Reset"))); resetButton.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { resetAction(); } }); resetButton.setStylePrimaryName("topButton"); buttonPanel.add(runStopButton = new Button(LSHTML("<Strong>RUN</Strong> / Stop"))); runStopButton.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { setSimRunning(!simIsRunning()); } }); /* dumpMatrixButton = new Button("Dump Matrix"); dumpMatrixButton.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { dumpMatrix = true; }}); verticalPanel.add(dumpMatrixButton);// IES for debugging */ if (LoadFile.isSupported()) verticalPanel.add(loadFileInput = new LoadFile(this)); Label l; verticalPanel.add(l = new Label(LS("Simulation Speed"))); l.addStyleName("topSpace"); // was max of 140 verticalPanel.add( speedBar = new Scrollbar(Scrollbar.HORIZONTAL, 3, 1, 0, 260)); verticalPanel.add( l = new Label(LS("Current Speed"))); l.addStyleName("topSpace"); currentBar = new Scrollbar(Scrollbar.HORIZONTAL, 50, 1, 1, 100); verticalPanel.add(currentBar); verticalPanel.add(powerLabel = new Label (LS("Power Brightness"))); powerLabel.addStyleName("topSpace"); verticalPanel.add(powerBar = new Scrollbar(Scrollbar.HORIZONTAL, 50, 1, 1, 100)); setPowerBarEnable(); // verticalPanel.add(new Label("")); // Font f = new Font("SansSerif", 0, 10); l = new Label(LS("Current Circuit:")); l.addStyleName("topSpace"); // l.setFont(f); titleLabel = new Label("Label"); // titleLabel.setFont(f); verticalPanel.add(l); verticalPanel.add(titleLabel); verticalPanel.add(iFrame = new Frame("iframe.html")); iFrame.setWidth(VERTICALPANELWIDTH+"px"); iFrame.setHeight("100 px"); iFrame.getElement().setAttribute("scrolling", "no"); setGrid(); elmList = new Vector<CircuitElm>(); // setupList = new Vector(); undoStack = new Vector<String>(); redoStack = new Vector<String>(); scopes = new Scope[20]; scopeColCount = new int[20]; scopeCount = 0; random = new Random(); // cv.setBackground(Color.black); // cv.setForeground(Color.lightGray); elmMenuBar = new MenuBar(true); elmMenuBar.addItem(elmEditMenuItem = new MenuItem(LS("Edit"),new MyCommand("elm","edit"))); elmMenuBar.addItem(elmScopeMenuItem = new MenuItem(LS("View in Scope"), new MyCommand("elm","viewInScope"))); elmMenuBar.addItem(elmCutMenuItem = new MenuItem(LS("Cut"),new MyCommand("elm","cut"))); elmMenuBar.addItem(elmCopyMenuItem = new MenuItem(LS("Copy"),new MyCommand("elm","copy"))); elmMenuBar.addItem(elmDeleteMenuItem = new MenuItem(LS("Delete"),new MyCommand("elm","delete"))); elmMenuBar.addItem( new MenuItem(LS("Duplicate"),new MyCommand("elm","duplicate"))); elmMenuBar.addItem(elmFlipMenuItem = new MenuItem(LS("Swap Terminals"),new MyCommand("elm","flip"))); scopeMenuBar = buildScopeMenu(false); transScopeMenuBar = buildScopeMenu(true); if (startCircuitText != null) { getSetupList(false); readSetup(startCircuitText, true); } else { if (stopMessage == null && startCircuitLink!=null) { readSetup(null, 0, false, true); getSetupList(false); ImportFromDropboxDialog.setSim(this); ImportFromDropboxDialog.doImportDropboxLink(startCircuitLink, false); } else { readSetup(null, 0, false, true); if (stopMessage == null && startCircuit != null) { getSetupList(false); readSetupFile(startCircuit, startLabel, true); } else getSetupList(true); } } enableUndoRedo(); enablePaste(); setiFrameHeight(); cv.addMouseDownHandler(this); cv.addMouseMoveHandler(this); cv.addMouseOutHandler(this); cv.addMouseUpHandler(this); cv.addClickHandler(this); cv.addDoubleClickHandler(this); doTouchHandlers(cv.getCanvasElement()); cv.addDomHandler(this, ContextMenuEvent.getType()); menuBar.addDomHandler(new ClickHandler() { public void onClick(ClickEvent event) { doMainMenuChecks(); } }, ClickEvent.getType()); Event.addNativePreviewHandler(this); cv.addMouseWheelHandler(this); setSimRunning(true); // setup timer timer.scheduleRepeating(FASTTIMER); } MenuItem menuItemWithShortcut(String text, String shortcut, MyCommand cmd) { final String edithtml="<div style=\"display:inline-block;width:80px;\">"; String sn=edithtml + text + "</div>" + shortcut; return new MenuItem(SafeHtmlUtils.fromTrustedString(sn), cmd); } boolean getOptionFromStorage(String key, boolean val) { Storage stor = Storage.getLocalStorageIfSupported(); if (stor == null) return val; String s = stor.getItem(key); if (s == null) return val; return s == "true"; } void setOptionInStorage(String key, boolean val) { Storage stor = Storage.getLocalStorageIfSupported(); if (stor == null) return; stor.setItem(key, val ? "true" : "false"); } // install touch handlers // don't feel like rewriting this in java. Anyway, java doesn't let us create mouse // events and dispatch them. native void doTouchHandlers(CanvasElement cv) /*-{ // Set up touch events for mobile, etc var lastTap; var tmout; var sim = this; cv.addEventListener("touchstart", function (e) { mousePos = getTouchPos(cv, e); var touch = e.touches[0]; var etype = "mousedown"; clearTimeout(tmout); if (e.timeStamp-lastTap < 300) { etype = "dblclick"; } else { tmout = setTimeout(function() { sim.@com.lushprojects.circuitjs1.client.CirSim::longPress()(); }, 500); } lastTap = e.timeStamp; var mouseEvent = new MouseEvent(etype, { clientX: touch.clientX, clientY: touch.clientY }); e.preventDefault(); cv.dispatchEvent(mouseEvent); }, false); cv.addEventListener("touchend", function (e) { var mouseEvent = new MouseEvent("mouseup", {}); e.preventDefault(); clearTimeout(tmout); cv.dispatchEvent(mouseEvent); }, false); cv.addEventListener("touchmove", function (e) { var touch = e.touches[0]; var mouseEvent = new MouseEvent("mousemove", { clientX: touch.clientX, clientY: touch.clientY }); e.preventDefault(); clearTimeout(tmout); cv.dispatchEvent(mouseEvent); }, false); // Get the position of a touch relative to the canvas function getTouchPos(canvasDom, touchEvent) { var rect = canvasDom.getBoundingClientRect(); return { x: touchEvent.touches[0].clientX - rect.left, y: touchEvent.touches[0].clientY - rect.top }; } }-*/; boolean shown = false; public void composeMainMenu(MenuBar mainMenuBar) { mainMenuBar.addItem(getClassCheckItem(LS("Add Wire"), "WireElm")); mainMenuBar.addItem(getClassCheckItem(LS("Add Resistor"), "ResistorElm")); MenuBar passMenuBar = new MenuBar(true); passMenuBar.addItem(getClassCheckItem(LS("Add Capacitor"), "CapacitorElm")); passMenuBar.addItem(getClassCheckItem(LS("Add Capacitor (polarized)"), "PolarCapacitorElm")); passMenuBar.addItem(getClassCheckItem(LS("Add Inductor"), "InductorElm")); passMenuBar.addItem(getClassCheckItem(LS("Add Switch"), "SwitchElm")); passMenuBar.addItem(getClassCheckItem(LS("Add Push Switch"), "PushSwitchElm")); passMenuBar.addItem(getClassCheckItem(LS("Add SPDT Switch"), "Switch2Elm")); passMenuBar.addItem(getClassCheckItem(LS("Add Potentiometer"), "PotElm")); passMenuBar.addItem(getClassCheckItem(LS("Add Transformer"), "TransformerElm")); passMenuBar.addItem(getClassCheckItem(LS("Add Tapped Transformer"), "TappedTransformerElm")); passMenuBar.addItem(getClassCheckItem(LS("Add Transmission Line"), "TransLineElm")); passMenuBar.addItem(getClassCheckItem(LS("Add Relay"), "RelayElm")); passMenuBar.addItem(getClassCheckItem(LS("Add Memristor"), "MemristorElm")); passMenuBar.addItem(getClassCheckItem(LS("Add Spark Gap"), "SparkGapElm")); mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+LS(" </div>Passive Components")), passMenuBar); MenuBar inputMenuBar = new MenuBar(true); inputMenuBar.addItem(getClassCheckItem(LS("Add Ground"), "GroundElm")); inputMenuBar.addItem(getClassCheckItem(LS("Add Voltage Source (2-terminal)"), "DCVoltageElm")); inputMenuBar.addItem(getClassCheckItem(LS("Add A/C Voltage Source (2-terminal)"), "ACVoltageElm")); inputMenuBar.addItem(getClassCheckItem(LS("Add Voltage Source (1-terminal)"), "RailElm")); inputMenuBar.addItem(getClassCheckItem(LS("Add A/C Voltage Source (1-terminal)"), "ACRailElm")); inputMenuBar.addItem(getClassCheckItem(LS("Add Square Wave Source (1-terminal)"), "SquareRailElm")); inputMenuBar.addItem(getClassCheckItem(LS("Add Clock"), "ClockElm")); inputMenuBar.addItem(getClassCheckItem(LS("Add A/C Sweep"), "SweepElm")); inputMenuBar.addItem(getClassCheckItem(LS("Add Variable Voltage"), "VarRailElm")); inputMenuBar.addItem(getClassCheckItem(LS("Add Antenna"), "AntennaElm")); inputMenuBar.addItem(getClassCheckItem(LS("Add AM Source"), "AMElm")); inputMenuBar.addItem(getClassCheckItem(LS("Add FM Source"), "FMElm")); inputMenuBar.addItem(getClassCheckItem(LS("Add Current Source"), "CurrentElm")); inputMenuBar.addItem(getClassCheckItem(LS("Add Noise Generator"), "NoiseElm")); mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+LS(" </div>Inputs and Sources")), inputMenuBar); MenuBar outputMenuBar = new MenuBar(true); outputMenuBar.addItem(getClassCheckItem(LS("Add Analog Output"), "OutputElm")); outputMenuBar.addItem(getClassCheckItem(LS("Add LED"), "LEDElm")); outputMenuBar.addItem(getClassCheckItem(LS("Add Lamp"), "LampElm")); outputMenuBar.addItem(getClassCheckItem(LS("Add Text"), "TextElm")); outputMenuBar.addItem(getClassCheckItem(LS("Add Box"), "BoxElm")); outputMenuBar.addItem(getClassCheckItem(LS("Add Voltmeter/Scobe Probe"), "ProbeElm")); outputMenuBar.addItem(getClassCheckItem(LS("Add Labeled Node"), "LabeledNodeElm")); outputMenuBar.addItem(getClassCheckItem(LS("Add Test Point"), "TestPointElm")); outputMenuBar.addItem(getClassCheckItem(LS("Add Ammeter"), "AmmeterElm")); outputMenuBar.addItem(getClassCheckItem(LS("Add Data Export"), "DataRecorderElm")); outputMenuBar.addItem(getClassCheckItem(LS("Add Audio Output"), "AudioOutputElm")); mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+LS(" </div>Outputs and Labels")), outputMenuBar); MenuBar activeMenuBar = new MenuBar(true); activeMenuBar.addItem(getClassCheckItem(LS("Add Diode"), "DiodeElm")); activeMenuBar.addItem(getClassCheckItem(LS("Add Zener Diode"), "ZenerElm")); activeMenuBar.addItem(getClassCheckItem(LS("Add Transistor (bipolar, NPN)"), "NTransistorElm")); activeMenuBar.addItem(getClassCheckItem(LS("Add Transistor (bipolar, PNP)"), "PTransistorElm")); activeMenuBar.addItem(getClassCheckItem(LS("Add MOSFET (N-Channel)"), "NMosfetElm")); activeMenuBar.addItem(getClassCheckItem(LS("Add MOSFET (P-Channel)"), "PMosfetElm")); activeMenuBar.addItem(getClassCheckItem(LS("Add JFET (N-Channel)"), "NJfetElm")); activeMenuBar.addItem(getClassCheckItem(LS("Add JFET (P-Channel)"), "PJfetElm")); activeMenuBar.addItem(getClassCheckItem(LS("Add SCR"), "SCRElm")); activeMenuBar.addItem(getClassCheckItem(LS("Add Darlington Pair (NPN)"), "NDarlingtonElm")); activeMenuBar.addItem(getClassCheckItem(LS("Add Darlington Pair (PNP)"), "PDarlingtonElm")); // activeMenuBar.addItem(getClassCheckItem("Add Varactor/Varicap", "VaractorElm")); activeMenuBar.addItem(getClassCheckItem(LS("Add Tunnel Diode"), "TunnelDiodeElm")); activeMenuBar.addItem(getClassCheckItem(LS("Add Triode"), "TriodeElm")); // activeMenuBar.addItem(getClassCheckItem("Add Diac", "DiacElm")); // activeMenuBar.addItem(getClassCheckItem("Add Triac", "TriacElm")); // activeMenuBar.addItem(getClassCheckItem("Add Photoresistor", "PhotoResistorElm")); // activeMenuBar.addItem(getClassCheckItem("Add Thermistor", "ThermistorElm")); mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+LS(" </div>Active Components")), activeMenuBar); MenuBar activeBlocMenuBar = new MenuBar(true); activeBlocMenuBar.addItem(getClassCheckItem(LS("Add Op Amp (- on top)"), "OpAmpElm")); activeBlocMenuBar.addItem(getClassCheckItem(LS("Add Op Amp (+ on top)"), "OpAmpSwapElm")); activeBlocMenuBar.addItem(getClassCheckItem(LS("Add Analog Switch (SPST)"), "AnalogSwitchElm")); activeBlocMenuBar.addItem(getClassCheckItem(LS("Add Analog Switch (SPDT)"), "AnalogSwitch2Elm")); activeBlocMenuBar.addItem(getClassCheckItem(LS("Add Tristate Buffer"), "TriStateElm")); activeBlocMenuBar.addItem(getClassCheckItem(LS("Add Schmitt Trigger"), "SchmittElm")); activeBlocMenuBar.addItem(getClassCheckItem(LS("Add Schmitt Trigger (Inverting)"), "InvertingSchmittElm")); activeBlocMenuBar.addItem(getClassCheckItem(LS("Add CCII+"), "CC2Elm")); activeBlocMenuBar.addItem(getClassCheckItem(LS("Add CCII-"), "CC2NegElm")); activeBlocMenuBar.addItem(getClassCheckItem(LS("Add Comparator (Hi-Z/GND output)"), "ComparatorElm")); activeBlocMenuBar.addItem(getClassCheckItem(LS("Add OTA (LM13700 style)"), "OTAElm")); activeBlocMenuBar.addItem(getClassCheckItem(LS("Add Voltage-Controlled Voltage Source"), "VCVSElm")); activeBlocMenuBar.addItem(getClassCheckItem(LS("Add Voltage-Controlled Current Source"), "VCCSElm")); activeBlocMenuBar.addItem(getClassCheckItem(LS("Add Current-Controlled Voltage Source"), "CCVSElm")); activeBlocMenuBar.addItem(getClassCheckItem(LS("Add Current-Controlled Current Source"), "CCCSElm")); mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+LS(" </div>Active Building Blocks")), activeBlocMenuBar); MenuBar gateMenuBar = new MenuBar(true); gateMenuBar.addItem(getClassCheckItem(LS("Add Logic Input"), "LogicInputElm")); gateMenuBar.addItem(getClassCheckItem(LS("Add Logic Output"), "LogicOutputElm")); gateMenuBar.addItem(getClassCheckItem(LS("Add Inverter"), "InverterElm")); gateMenuBar.addItem(getClassCheckItem(LS("Add NAND Gate"), "NandGateElm")); gateMenuBar.addItem(getClassCheckItem(LS("Add NOR Gate"), "NorGateElm")); gateMenuBar.addItem(getClassCheckItem(LS("Add AND Gate"), "AndGateElm")); gateMenuBar.addItem(getClassCheckItem(LS("Add OR Gate"), "OrGateElm")); gateMenuBar.addItem(getClassCheckItem(LS("Add XOR Gate"), "XorGateElm")); mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+LS(" </div>Logic Gates, Input and Output")), gateMenuBar); MenuBar chipMenuBar = new MenuBar(true); chipMenuBar.addItem(getClassCheckItem(LS("Add D Flip-Flop"), "DFlipFlopElm")); chipMenuBar.addItem(getClassCheckItem(LS("Add JK Flip-Flop"), "JKFlipFlopElm")); chipMenuBar.addItem(getClassCheckItem(LS("Add T Flip-Flop"), "TFlipFlopElm")); chipMenuBar.addItem(getClassCheckItem(LS("Add 7 Segment LED"), "SevenSegElm")); chipMenuBar.addItem(getClassCheckItem(LS("Add 7 Segment Decoder"), "SevenSegDecoderElm")); chipMenuBar.addItem(getClassCheckItem(LS("Add Multiplexer"), "MultiplexerElm")); chipMenuBar.addItem(getClassCheckItem(LS("Add Demultiplexer"), "DeMultiplexerElm")); chipMenuBar.addItem(getClassCheckItem(LS("Add SIPO shift register"), "SipoShiftElm")); chipMenuBar.addItem(getClassCheckItem(LS("Add PISO shift register"), "PisoShiftElm")); chipMenuBar.addItem(getClassCheckItem(LS("Add Counter"), "CounterElm")); chipMenuBar.addItem(getClassCheckItem(LS("Add Decade Counter"), "DecadeElm")); chipMenuBar.addItem(getClassCheckItem(LS("Add Latch"), "LatchElm")); //chipMenuBar.addItem(getClassCheckItem("Add Static RAM", "SRAMElm")); chipMenuBar.addItem(getClassCheckItem(LS("Add Sequence generator"), "SeqGenElm")); chipMenuBar.addItem(getClassCheckItem(LS("Add Full Adder"), "FullAdderElm")); chipMenuBar.addItem(getClassCheckItem(LS("Add Half Adder"), "HalfAdderElm")); chipMenuBar.addItem(getClassCheckItem(LS("Add Custom Logic"), "UserDefinedLogicElm")); mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+LS(" </div>Digital Chips")), chipMenuBar); MenuBar achipMenuBar = new MenuBar(true); achipMenuBar.addItem(getClassCheckItem(LS("Add 555 Timer"), "TimerElm")); achipMenuBar.addItem(getClassCheckItem(LS("Add Phase Comparator"), "PhaseCompElm")); achipMenuBar.addItem(getClassCheckItem(LS("Add DAC"), "DACElm")); achipMenuBar.addItem(getClassCheckItem(LS("Add ADC"), "ADCElm")); achipMenuBar.addItem(getClassCheckItem(LS("Add VCO"), "VCOElm")); achipMenuBar.addItem(getClassCheckItem(LS("Add Monostable"), "MonostableElm")); mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+LS(" </div>Analog and Hybrid Chips")), achipMenuBar); MenuBar otherMenuBar = new MenuBar(true); CheckboxMenuItem mi; otherMenuBar.addItem(mi=getClassCheckItem(LS("Drag All"), "DragAll")); mi.addShortcut(LS("(Alt-drag)")); otherMenuBar.addItem(mi=getClassCheckItem(LS("Drag Row"), "DragRow")); mi.addShortcut(LS("(A-S-drag)")); otherMenuBar.addItem(mi=getClassCheckItem(LS("Drag Column"), "DragColumn")); mi.addShortcut(isMac ? LS("(A-Cmd-drag)") : LS("(A-M-drag)")); otherMenuBar.addItem(getClassCheckItem(LS("Drag Selected"), "DragSelected")); otherMenuBar.addItem(mi=getClassCheckItem(LS("Drag Post"), "DragPost")); mi.addShortcut("(" + ctrlMetaKey + "-drag)"); mainMenuBar.addItem(SafeHtmlUtils.fromTrustedString(CheckboxMenuItem.checkBoxHtml+LS(" </div>Drag")), otherMenuBar); mainMenuBar.addItem(mi=getClassCheckItem(LS("Select/Drag Sel"), "Select")); mi.addShortcut(LS("(space or Shift-drag)")); } public void setiFrameHeight() { if (iFrame==null) return; int i; int cumheight=0; for (i=0; i < verticalPanel.getWidgetIndex(iFrame); i++) { if (verticalPanel.getWidget(i) !=loadFileInput) { cumheight=cumheight+verticalPanel.getWidget(i).getOffsetHeight(); if (verticalPanel.getWidget(i).getStyleName().contains("topSpace")) cumheight+=12; } } int ih=RootLayoutPanel.get().getOffsetHeight()-MENUBARHEIGHT-cumheight; if (ih<0) ih=0; iFrame.setHeight(ih+"px"); } MenuBar buildScopeMenu(boolean t) { MenuBar m = new MenuBar(true); m.addItem(new CheckboxAlignedMenuItem(LS("Remove Scope"),new MyCommand("scopepop", "remove"))); m.addItem(new CheckboxAlignedMenuItem(LS("Speed 2x"), new MyCommand("scopepop", "speed2"))); m.addItem(new CheckboxAlignedMenuItem(LS("Speed 1/2x"), new MyCommand("scopepop", "speed1/2"))); // m.addItem(new CheckboxAlignedMenuItem(LS("Scale 2x"), new MyCommand("scopepop", "scale"))); CheckboxMenuItem mi; m.addItem(mi = new CheckboxMenuItem(LS("Max Scale"), new MyCommand("scopepop", "maxscale"))); if (t) scopeMaxScaleTransMenuItem = mi; else scopeMaxScaleMenuItem = mi; m.addItem(new CheckboxAlignedMenuItem(LS("Stack"), new MyCommand("scopepop", "stack"))); m.addItem(new CheckboxAlignedMenuItem(LS("Unstack"), new MyCommand("scopepop", "unstack"))); m.addItem(new CheckboxAlignedMenuItem(LS("Combine"), new MyCommand("scopepop", "combine"))); if (!t) m.addItem(scopeRemovePlotMenuItem = new CheckboxAlignedMenuItem(LS("Remove Plot"),new MyCommand("scopepop", "removeplot"))); m.addItem(new CheckboxAlignedMenuItem(LS("Reset"), new MyCommand("scopepop", "reset"))); if (t) { m.addItem(scopeIbMenuItem = new CheckboxMenuItem(LS("Show Ib"), new MyCommand("scopepop", "showib"))); m.addItem(scopeIcMenuItem = new CheckboxMenuItem(LS("Show Ic"), new MyCommand("scopepop", "showic"))); m.addItem(scopeIeMenuItem = new CheckboxMenuItem(LS("Show Ie"), new MyCommand("scopepop", "showie"))); m.addItem(scopeVbeMenuItem = new CheckboxMenuItem(LS("Show Vbe"), new MyCommand("scopepop", "showvbe"))); m.addItem(scopeVbcMenuItem = new CheckboxMenuItem(LS("Show Vbc"), new MyCommand("scopepop", "showvbc"))); m.addItem(scopeVceMenuItem = new CheckboxMenuItem(LS("Show Vce"), new MyCommand("scopepop", "showvce"))); m.addItem(scopeVceIcMenuItem = new CheckboxMenuItem(LS("Show Vce vs Ic"), new MyCommand("scopepop", "showvcevsic"))); } else { m.addItem(scopeVMenuItem = new CheckboxMenuItem(LS("Show Voltage"), new MyCommand("scopepop", "showvoltage"))); m.addItem(scopeIMenuItem = new CheckboxMenuItem(LS("Show Current"), new MyCommand("scopepop", "showcurrent"))); m.addItem(scopePowerMenuItem = new CheckboxMenuItem(LS("Show Power Consumed"), new MyCommand("scopepop", "showpower"))); m.addItem(scopeScaleMenuItem = new CheckboxMenuItem(LS("Show Scale"), new MyCommand("scopepop", "showscale"))); m.addItem(scopeMaxMenuItem = new CheckboxMenuItem(LS("Show Peak Value"), new MyCommand("scopepop", "showpeak"))); m.addItem(scopeMinMenuItem = new CheckboxMenuItem(LS("Show Negative Peak Value"), new MyCommand("scopepop", "shownegpeak"))); m.addItem(scopeFreqMenuItem = new CheckboxMenuItem(LS("Show Frequency"), new MyCommand("scopepop", "showfreq"))); m.addItem(scopeFFTMenuItem = new CheckboxMenuItem(LS("Show Spectrum"), new MyCommand("scopepop", "showfft"))); m.addItem(scopeRMSMenuItem = new CheckboxMenuItem(LS("Show RMS Average"), new MyCommand("scopepop", "showrms"))); m.addItem(scopeVIMenuItem = new CheckboxMenuItem(LS("Show V vs I"), new MyCommand("scopepop", "showvvsi"))); m.addItem(scopeXYMenuItem = new CheckboxMenuItem(LS("Plot X/Y"), new MyCommand("scopepop", "plotxy"))); m.addItem(scopeSelectYMenuItem = new CheckboxAlignedMenuItem(LS("Select Y"), new MyCommand("scopepop", "selecty"))); m.addItem(scopeResistMenuItem = new CheckboxMenuItem(LS("Show Resistance"), new MyCommand("scopepop", "showresistance"))); } return m; } CheckboxMenuItem getClassCheckItem(String s, String t) { // try { // Class c = Class.forName(t); String shortcut=""; CircuitElm elm = constructElement(t, 0, 0); CheckboxMenuItem mi; // register(c, elm); if ( elm!=null ) { if (elm.needsShortcut() ) { shortcut += (char)elm.getShortcut(); shortcuts[elm.getShortcut()]=t; } elm.delete(); } // else // GWT.log("Coudn't create class: "+t); // } catch (Exception ee) { // ee.printStackTrace(); // } if (shortcut=="") mi= new CheckboxMenuItem(s); else mi = new CheckboxMenuItem(s, shortcut); mi.setScheduledCommand(new MyCommand("main", t) ); mainMenuItems.add(mi); mainMenuItemNames.add(t); return mi; } void centreCircuit() { Rectangle bounds = getCircuitBounds(); // add some space on edges because bounds calculation is not perfect double scale = 1; if (bounds != null) scale = Math.min(circuitArea.width /(double)(bounds.width+140), circuitArea.height/(double)(bounds.height+100)); scale = Math.min(scale, 1.5); // Limit scale so we don't create enormous circuits in big windows // calculate transform so circuit fills most of screen transform[0] = transform[3] = scale; transform[1] = transform[2] = transform[4] = transform[5] = 0; if (bounds != null) { transform[4] = (circuitArea.width -bounds.width *scale)/2 - bounds.x*scale; transform[5] = (circuitArea.height-bounds.height*scale)/2 - bounds.y*scale; } } // get circuit bounds. remember this doesn't use setBbox(). That is calculated when we draw // the circuit, but this needs to be ready before we first draw it, so we use this crude method Rectangle getCircuitBounds() { int i; int minx = 1000, maxx = 0, miny = 1000, maxy = 0; for (i = 0; i != elmList.size(); i++) { CircuitElm ce = getElm(i); // centered text causes problems when trying to center the circuit, // so we special-case it here if (!ce.isCenteredText()) { minx = min(ce.x, min(ce.x2, minx)); maxx = max(ce.x, max(ce.x2, maxx)); } miny = min(ce.y, min(ce.y2, miny)); maxy = max(ce.y, max(ce.y2, maxy)); } if (minx > maxx) return null; return new Rectangle(minx, miny, maxx-minx, maxy-miny); } static final int resct = 6; long lastTime = 0, lastFrameTime, lastIterTime, secTime = 0; int frames = 0; int steps = 0; int framerate = 0, steprate = 0; static CirSim theSim; public void setSimRunning(boolean s) { if (s) { simRunning = true; runStopButton.setHTML(LSHTML("<strong>RUN</strong> / Stop")); runStopButton.setStylePrimaryName("topButton"); } else { simRunning = false; runStopButton.setHTML(LSHTML("Run / <strong>STOP</strong>")); runStopButton.setStylePrimaryName("topButton-red"); } } public boolean simIsRunning() { return simRunning; } // ***************************************************************** // UPDATE CIRCUIT public void updateCircuit() { long mystarttime; long myrunstarttime; long mydrawstarttime; // if (winSize == null || winSize.width == 0) // return; mystarttime=System.currentTimeMillis(); boolean didAnalyze = analyzeFlag; if (analyzeFlag) { analyzeCircuit(); analyzeFlag = false; } // if (editDialog != null && editDialog.elm instanceof CircuitElm) // mouseElm = (CircuitElm) (editDialog.elm); if (stopElm != null && stopElm != mouseElm) stopElm.setMouseElm(true); setupScopes(); Graphics g=new Graphics(backcontext); CircuitElm.selectColor = Color.cyan; if (printableCheckItem.getState()) { CircuitElm.whiteColor = Color.black; CircuitElm.lightGrayColor = Color.black; g.setColor(Color.white); } else { CircuitElm.whiteColor = Color.white; CircuitElm.lightGrayColor = Color.lightGray; g.setColor(Color.black); } g.fillRect(0, 0, g.context.getCanvas().getWidth(), g.context.getCanvas().getHeight()); myrunstarttime=System.currentTimeMillis(); if (simRunning) { try { runCircuit(didAnalyze); } catch (Exception e) { console("exception in runCircuit " + e); e.printStackTrace(); return; } myruntime+=System.currentTimeMillis()-myrunstarttime; } long sysTime = System.currentTimeMillis(); if (simRunning) { if (lastTime != 0) { int inc = (int) (sysTime - lastTime); double c = currentBar.getValue(); c = java.lang.Math.exp(c / 3.5 - 14.2); CircuitElm.currentMult = 1.7 * inc * c; if (!conventionCheckItem.getState()) CircuitElm.currentMult = -CircuitElm.currentMult; } lastTime = sysTime; } else lastTime = 0; if (sysTime - secTime >= 1000) { framerate = frames; steprate = steps; frames = 0; steps = 0; secTime = sysTime; } CircuitElm.powerMult = Math.exp(powerBar.getValue()/4.762-7); int i; // Font oldfont = g.getFont(); Font oldfont = CircuitElm.unitsFont; g.setFont(oldfont); // this causes bad behavior on Chrome 55 // g.clipRect(0, 0, circuitArea.width, circuitArea.height); mydrawstarttime=System.currentTimeMillis(); g.context.setLineCap(LineCap.ROUND); // draw elements backcontext.setTransform(transform[0], transform[1], transform[2], transform[3], transform[4], transform[5]); for (i = 0; i != elmList.size(); i++) { if (powerCheckItem.getState()) g.setColor(Color.gray); /*else if (conductanceCheckItem.getState()) g.setColor(Color.white);*/ getElm(i).draw(g); } mydrawtime+=System.currentTimeMillis()-mydrawstarttime; // draw posts normally if (mouseMode != CirSim.MODE_DRAG_ROW && mouseMode != CirSim.MODE_DRAG_COLUMN) { for (i = 0; i != postDrawList.size(); i++) CircuitElm.drawPost(g, postDrawList.get(i)); } // for some mouse modes, what matters is not the posts but the endpoints (which are only // the same for 2-terminal elements). We draw those now if needed if (tempMouseMode == MODE_DRAG_ROW || tempMouseMode == MODE_DRAG_COLUMN || tempMouseMode == MODE_DRAG_POST || tempMouseMode == MODE_DRAG_SELECTED) for (i = 0; i != elmList.size(); i++) { CircuitElm ce = getElm(i); // ce.drawPost(g, ce.x , ce.y ); // ce.drawPost(g, ce.x2, ce.y2); if (ce!=mouseElm || tempMouseMode!=MODE_DRAG_POST) { g.setColor(Color.gray); g.fillOval(ce.x-3, ce.y-3, 7, 7); g.fillOval(ce.x2-3, ce.y2-3, 7, 7); } else { ce.drawHandles(g, Color.cyan); } } // draw handles for elm we're creating if (tempMouseMode==MODE_SELECT && mouseElm!=null) { mouseElm.drawHandles(g, Color.cyan); } // draw handles for elm we're dragging if (dragElm != null && (dragElm.x != dragElm.x2 || dragElm.y != dragElm.y2)) { dragElm.draw(g); dragElm.drawHandles(g, Color.cyan); } // draw bad connections. do this last so they will not be overdrawn. for (i = 0; i != badConnectionList.size(); i++) { Point cn = badConnectionList.get(i); g.setColor(Color.red); g.fillOval(cn.x-3, cn.y-3, 7, 7); } if (selectedArea != null) { g.setColor(CircuitElm.selectColor); g.drawRect(selectedArea.x, selectedArea.y, selectedArea.width, selectedArea.height); } if (crossHairCheckItem.getState() && mouseCursorX>=0 && mouseCursorX <= circuitArea.width && mouseCursorY <= circuitArea.height) { g.setColor(Color.gray); int x = snapGrid(inverseTransformX(mouseCursorX)); int y = snapGrid(inverseTransformY(mouseCursorY)); g.drawLine(x, inverseTransformY(0), x, inverseTransformY(circuitArea.height)); g.drawLine(inverseTransformX(0), y, inverseTransformX(circuitArea.width), y); } backcontext.setTransform(1, 0, 0, 1, 0, 0); if (printableCheckItem.getState()) g.setColor(Color.white); else g.setColor(Color.black); g.fillRect(0, circuitArea.height, circuitArea.width, cv.getCoordinateSpaceHeight()-circuitArea.height); // g.restore(); g.setFont(oldfont); int ct = scopeCount; if (stopMessage != null) ct = 0; for (i = 0; i != ct; i++) scopes[i].draw(g); if (mouseWasOverSplitter) { g.setColor(Color.cyan); g.setLineWidth(4.0); g.drawLine(0, circuitArea.height-2, circuitArea.width, circuitArea.height-2); g.setLineWidth(1.0); } g.setColor(CircuitElm.whiteColor); if (stopMessage != null) { g.drawString(stopMessage, 10, circuitArea.height-10); } else { String info[] = new String[10]; if (mouseElm != null) { if (mousePost == -1) { mouseElm.getInfo(info); info[0] = LS(info[0]); if (info[1] != null) info[1] = LS(info[1]); } else info[0] = "V = " + CircuitElm.getUnitText(mouseElm.getPostVoltage(mousePost), "V"); /* //shownodes for (i = 0; i != mouseElm.getPostCount(); i++) info[0] += " " + mouseElm.nodes[i]; if (mouseElm.getVoltageSourceCount() > 0) info[0] += ";" + (mouseElm.getVoltageSource()+nodeList.size()); */ } else { info[0] = "t = " + CircuitElm.getUnitText(t, "s"); info[1] = LS("time step = ") + CircuitElm.getUnitText(timeStep, "s"); } if (hintType != -1) { for (i = 0; info[i] != null; i++) ; String s = getHint(); if (s == null) hintType = -1; else info[i] = s; } int x = 0; if (ct != 0) x = scopes[ct-1].rightEdge() + 20; x = max(x, cv.getCoordinateSpaceWidth()*2/3); // x=cv.getCoordinateSpaceWidth()*2/3; // count lines of data for (i = 0; info[i] != null; i++) ; int badnodes = badConnectionList.size(); if (badnodes > 0) info[i++] = badnodes + ((badnodes == 1) ? LS(" bad connection") : LS(" bad connections")); int ybase = circuitArea.height; for (i = 0; info[i] != null; i++) g.drawString(info[i], x, ybase+15*(i+1)); } if (stopElm != null && stopElm != mouseElm) stopElm.setMouseElm(false); frames++; g.setColor(Color.white); // g.drawString("Framerate: " + CircuitElm.showFormat.format(framerate), 10, 10); // g.drawString("Steprate: " + CircuitElm.showFormat.format(steprate), 10, 30); // g.drawString("Steprate/iter: " + CircuitElm.showFormat.format(steprate/getIterCount()), 10, 50); // g.drawString("iterc: " + CircuitElm.showFormat.format(getIterCount()), 10, 70); // g.drawString("Frames: "+ frames,10,90); // g.drawString("ms per frame (other): "+ CircuitElm.showFormat.format((mytime-myruntime-mydrawtime)/myframes),10,110); // g.drawString("ms per frame (sim): "+ CircuitElm.showFormat.format((myruntime)/myframes),10,130); // g.drawString("ms per frame (draw): "+ CircuitElm.showFormat.format((mydrawtime)/myframes),10,150); cvcontext.drawImage(backcontext.getCanvas(), 0.0, 0.0); lastFrameTime = lastTime; mytime=mytime+System.currentTimeMillis()-mystarttime; myframes++; } void setupScopes() { int i; // check scopes to make sure the elements still exist, and remove // unused scopes/columns int pos = -1; for (i = 0; i < scopeCount; i++) { if (scopes[i].needToRemove()) { int j; for (j = i; j != scopeCount; j++) scopes[j] = scopes[j+1]; scopeCount--; i--; continue; } if (scopes[i].position > pos+1) scopes[i].position = pos+1; pos = scopes[i].position; } while (scopeCount > 0 && scopes[scopeCount-1].getElm() == null) scopeCount--; int h = cv.getCoordinateSpaceHeight() - circuitArea.height; pos = 0; for (i = 0; i != scopeCount; i++) scopeColCount[i] = 0; for (i = 0; i != scopeCount; i++) { pos = max(scopes[i].position, pos); scopeColCount[scopes[i].position]++; } int colct = pos+1; int iw = infoWidth; if (colct <= 2) iw = iw*3/2; int w = (cv.getCoordinateSpaceWidth()-iw) / colct; int marg = 10; if (w < marg*2) w = marg*2; pos = -1; int colh = 0; int row = 0; int speed = 0; for (i = 0; i != scopeCount; i++) { Scope s = scopes[i]; if (s.position > pos) { pos = s.position; colh = h / scopeColCount[pos]; row = 0; speed = s.speed; } s.stackCount = scopeColCount[pos]; if (s.speed != speed) { s.speed = speed; s.resetGraph(); } Rectangle r = new Rectangle(pos*w, cv.getCoordinateSpaceHeight()-h+colh*row, w-marg, colh); row++; if (!r.equals(s.rect)) s.setRect(r); } } String getHint() { CircuitElm c1 = getElm(hintItem1); CircuitElm c2 = getElm(hintItem2); if (c1 == null || c2 == null) return null; if (hintType == HINT_LC) { if (!(c1 instanceof InductorElm)) return null; if (!(c2 instanceof CapacitorElm)) return null; InductorElm ie = (InductorElm) c1; CapacitorElm ce = (CapacitorElm) c2; return LS("res.f = ") + CircuitElm.getUnitText(1/(2*pi*Math.sqrt(ie.inductance* ce.capacitance)), "Hz"); } if (hintType == HINT_RC) { if (!(c1 instanceof ResistorElm)) return null; if (!(c2 instanceof CapacitorElm)) return null; ResistorElm re = (ResistorElm) c1; CapacitorElm ce = (CapacitorElm) c2; return "RC = " + CircuitElm.getUnitText(re.resistance*ce.capacitance, "s"); } if (hintType == HINT_3DB_C) { if (!(c1 instanceof ResistorElm)) return null; if (!(c2 instanceof CapacitorElm)) return null; ResistorElm re = (ResistorElm) c1; CapacitorElm ce = (CapacitorElm) c2; return LS("f.3db = ") + CircuitElm.getUnitText(1/(2*pi*re.resistance*ce.capacitance), "Hz"); } if (hintType == HINT_3DB_L) { if (!(c1 instanceof ResistorElm)) return null; if (!(c2 instanceof InductorElm)) return null; ResistorElm re = (ResistorElm) c1; InductorElm ie = (InductorElm) c2; return LS("f.3db = ") + CircuitElm.getUnitText(re.resistance/(2*pi*ie.inductance), "Hz"); } if (hintType == HINT_TWINT) { if (!(c1 instanceof ResistorElm)) return null; if (!(c2 instanceof CapacitorElm)) return null; ResistorElm re = (ResistorElm) c1; CapacitorElm ce = (CapacitorElm) c2; return LS("fc = ") + CircuitElm.getUnitText(1/(2*pi*re.resistance*ce.capacitance), "Hz"); } return null; } // public void toggleSwitch(int n) { // int i; // for (i = 0; i != elmList.size(); i++) { // CircuitElm ce = getElm(i); // if (ce instanceof SwitchElm) { // n--; // if (n == 0) { // ((SwitchElm) ce).toggle(); // analyzeFlag = true; // cv.repaint(); // return; // } // } // } // } void needAnalyze() { analyzeFlag = true; //cv.repaint(); } Vector<CircuitNode> nodeList; Vector<Point> postDrawList = new Vector<Point>(); Vector<Point> badConnectionList = new Vector<Point>(); CircuitElm voltageSources[]; public CircuitNode getCircuitNode(int n) { if (n >= nodeList.size()) return null; return nodeList.elementAt(n); } public CircuitElm getElm(int n) { if (n >= elmList.size()) return null; return elmList.elementAt(n); } public static native void console(String text) /*-{ console.log(text); }-*/; class NodeMapEntry { int node; NodeMapEntry() { node = -1; } NodeMapEntry(int n) { node = n; } } // map points to node numbers HashMap<Point,NodeMapEntry> nodeMap; HashMap<Point,Integer> postCountMap; class WireInfo { WireElm wire; Vector<CircuitElm> neighbors; int post; WireInfo(WireElm w) { wire = w; } } // info about each wire and its neighbors, used to calculate wire currents Vector<WireInfo> wireInfoList; // find groups of nodes connected by wires and map them to the same node. this speeds things // up considerably by reducing the size of the matrix void calculateWireClosure() { int i; nodeMap = new HashMap<Point,NodeMapEntry>(); // int mergeCount = 0; wireInfoList = new Vector<WireInfo>(); for (i = 0; i != elmList.size(); i++) { CircuitElm ce = getElm(i); if (!(ce instanceof WireElm)) continue; WireElm we = (WireElm) ce; we.hasWireInfo = false; wireInfoList.add(new WireInfo(we)); NodeMapEntry cn = nodeMap.get(ce.getPost(0)); NodeMapEntry cn2 = nodeMap.get(ce.getPost(1)); if (cn != null && cn2 != null) { // merge nodes; go through map and change all keys pointing to cn2 to point to cn for (Map.Entry<Point, NodeMapEntry> entry : nodeMap.entrySet()) { if (entry.getValue() == cn2) entry.setValue(cn); } // mergeCount++; continue; } if (cn != null) { nodeMap.put(ce.getPost(1), cn); continue; } if (cn2 != null) { nodeMap.put(ce.getPost(0), cn2); continue; } // new entry cn = new NodeMapEntry(); nodeMap.put(ce.getPost(0), cn); nodeMap.put(ce.getPost(1), cn); } // console("got " + (groupCount-mergeCount) + " groups with " + nodeMap.size() + " nodes " + mergeCount); } // generate info we need to calculate wire currents. Most other elements calculate currents using // the voltage on their terminal nodes. But wires have the same voltage at both ends, so we need // to use the neighbors' currents instead. boolean calcWireInfo() { int i; int moved = 0; for (i = 0; i != wireInfoList.size(); i++) { WireInfo wi = wireInfoList.get(i); WireElm wire = wi.wire; CircuitNode cn1 = nodeList.get(wire.getNode(0)); // both ends of wire have same node # int j; Vector<CircuitElm> neighbors0 = new Vector<CircuitElm>(); Vector<CircuitElm> neighbors1 = new Vector<CircuitElm>(); boolean isReady0 = true, isReady1 = true; // go through elements sharing a node with this wire (may be connected indirectly // by other wires, but at least it's faster than going through all elements) for (j = 0; j != cn1.links.size(); j++) { CircuitNodeLink cnl = cn1.links.get(j); CircuitElm ce = cnl.elm; if (ce == wire) continue; Point pt = cnl.elm.getPost(cnl.num); // is this a wire that doesn't have wire info yet? If so we can't use it. // That would create a circular dependency boolean notReady = (ce instanceof WireElm && !((WireElm) ce).hasWireInfo); // which post does this element connect to, if any? if (pt.x == wire.x && pt.y == wire.y) { neighbors0.add(ce); if (notReady) isReady0 = false; } else if (pt.x == wire.x2 && pt.y == wire.y2) { neighbors1.add(ce); if (notReady) isReady1 = false; } } // does one of the posts have all information necessary to calculate current if (isReady0) { wi.neighbors = neighbors0; wi.post = 0; wire.hasWireInfo = true; } else if (isReady1) { wi.neighbors = neighbors1; wi.post = 1; wire.hasWireInfo = true; } else { // move to the end of the list and try again later wireInfoList.add(wireInfoList.remove(i--)); moved++; // console("moved to end " + moved); if (moved > wireInfoList.size() * 2) { stop(LS("wire loop detected"), wire); return false; } } } return true; } void analyzeCircuit() { if (elmList.isEmpty()) { postDrawList = new Vector<Point>(); badConnectionList = new Vector<Point>(); return; } stopMessage = null; stopElm = null; int i, j; int vscount = 0; nodeList = new Vector<CircuitNode>(); postCountMap = new HashMap<Point,Integer>(); boolean gotGround = false; boolean gotRail = false; CircuitElm volt = null; calculateWireClosure(); //System.out.println("ac1"); // look for voltage or ground element for (i = 0; i != elmList.size(); i++) { CircuitElm ce = getElm(i); if (ce instanceof GroundElm) { gotGround = true; break; } if (ce instanceof RailElm) gotRail = true; if (volt == null && ce instanceof VoltageElm) volt = ce; } // if no ground, and no rails, then the voltage elm's first terminal // is ground if (!gotGround && volt != null && !gotRail) { CircuitNode cn = new CircuitNode(); Point pt = volt.getPost(0); nodeList.addElement(cn); // update node map NodeMapEntry cln = nodeMap.get(pt); if (cln != null) cln.node = 0; else nodeMap.put(pt, new NodeMapEntry(0)); } else { // otherwise allocate extra node for ground CircuitNode cn = new CircuitNode(); nodeList.addElement(cn); } //System.out.println("ac2"); // allocate nodes and voltage sources LabeledNodeElm.resetNodeList(); for (i = 0; i != elmList.size(); i++) { CircuitElm ce = getElm(i); int inodes = ce.getInternalNodeCount(); int ivs = ce.getVoltageSourceCount(); int posts = ce.getPostCount(); // allocate a node for each post and match posts to nodes for (j = 0; j != posts; j++) { Point pt = ce.getPost(j); Integer g = postCountMap.get(pt); postCountMap.put(pt, g == null ? 1 : g+1); NodeMapEntry cln = nodeMap.get(pt); // is this node not in map yet? or is the node number unallocated? // (we don't allocate nodes before this because changing the allocation order // of nodes changes circuit behavior and breaks backward compatibility; // the code below to connect unconnected nodes may connect a different node to ground) if (cln == null || cln.node == -1) { CircuitNode cn = new CircuitNode(); CircuitNodeLink cnl = new CircuitNodeLink(); cnl.num = j; cnl.elm = ce; cn.links.addElement(cnl); ce.setNode(j, nodeList.size()); if (cln != null) cln.node = nodeList.size(); else nodeMap.put(pt, new NodeMapEntry(nodeList.size())); nodeList.addElement(cn); } else { int n = cln.node; CircuitNodeLink cnl = new CircuitNodeLink(); cnl.num = j; cnl.elm = ce; getCircuitNode(n).links.addElement(cnl); ce.setNode(j, n); // if it's the ground node, make sure the node voltage is 0, // cause it may not get set later if (n == 0) ce.setNodeVoltage(j, 0); } } for (j = 0; j != inodes; j++) { CircuitNode cn = new CircuitNode(); cn.internal = true; CircuitNodeLink cnl = new CircuitNodeLink(); cnl.num = j+posts; cnl.elm = ce; cn.links.addElement(cnl); ce.setNode(cnl.num, nodeList.size()); nodeList.addElement(cn); } vscount += ivs; } makePostDrawList(); if (!calcWireInfo()) return; nodeMap = null; // done with this voltageSources = new CircuitElm[vscount]; vscount = 0; circuitNonLinear = false; //System.out.println("ac3"); // determine if circuit is nonlinear for (i = 0; i != elmList.size(); i++) { CircuitElm ce = getElm(i); if (ce.nonLinear()) circuitNonLinear = true; int ivs = ce.getVoltageSourceCount(); for (j = 0; j != ivs; j++) { voltageSources[vscount] = ce; ce.setVoltageSource(j, vscount++); } } voltageSourceCount = vscount; int matrixSize = nodeList.size()-1 + vscount; circuitMatrix = new double[matrixSize][matrixSize]; circuitRightSide = new double[matrixSize]; origMatrix = new double[matrixSize][matrixSize]; origRightSide = new double[matrixSize]; circuitMatrixSize = circuitMatrixFullSize = matrixSize; circuitRowInfo = new RowInfo[matrixSize]; circuitPermute = new int[matrixSize]; for (i = 0; i != matrixSize; i++) circuitRowInfo[i] = new RowInfo(); circuitNeedsMap = false; // stamp linear circuit elements for (i = 0; i != elmList.size(); i++) { CircuitElm ce = getElm(i); ce.stamp(); } //System.out.println("ac4"); // determine nodes that are not connected indirectly to ground boolean closure[] = new boolean[nodeList.size()]; boolean changed = true; closure[0] = true; while (changed) { changed = false; for (i = 0; i != elmList.size(); i++) { CircuitElm ce = getElm(i); if (ce instanceof WireElm) continue; // loop through all ce's nodes to see if they are connected // to other nodes not in closure for (j = 0; j < ce.getConnectionNodeCount(); j++) { if (!closure[ce.getConnectionNode(j)]) { if (ce.hasGroundConnection(j)) closure[ce.getConnectionNode(j)] = changed = true; continue; } int k; for (k = 0; k != ce.getConnectionNodeCount(); k++) { if (j == k) continue; int kn = ce.getConnectionNode(k); if (ce.getConnection(j, k) && !closure[kn]) { closure[kn] = true; changed = true; } } } } if (changed) continue; // connect one of the unconnected nodes to ground with a big resistor, then try again for (i = 0; i != nodeList.size(); i++) if (!closure[i] && !getCircuitNode(i).internal) { console("node " + i + " unconnected"); stampResistor(0, i, 1e8); closure[i] = true; changed = true; break; } } //System.out.println("ac5"); for (i = 0; i != elmList.size(); i++) { CircuitElm ce = getElm(i); // look for inductors with no current path if (ce instanceof InductorElm) { FindPathInfo fpi = new FindPathInfo(FindPathInfo.INDUCT, ce, ce.getNode(1)); // first try findPath with maximum depth of 5, to avoid slowdowns if (!fpi.findPath(ce.getNode(0), 5) && !fpi.findPath(ce.getNode(0))) { // console(ce + " no path"); ce.reset(); } } // look for current sources with no current path if (ce instanceof CurrentElm) { CurrentElm cur = (CurrentElm) ce; FindPathInfo fpi = new FindPathInfo(FindPathInfo.INDUCT, ce, ce.getNode(1)); if (!fpi.findPath(ce.getNode(0))) { cur.stampCurrentSource(true); } else cur.stampCurrentSource(false); } if (ce instanceof VCCSElm) { VCCSElm cur = (VCCSElm) ce; FindPathInfo fpi = new FindPathInfo(FindPathInfo.INDUCT, ce, cur.getOutputNode(0)); if (cur.hasCurrentOutput() && !fpi.findPath(cur.getOutputNode(1))) { cur.broken = true; } else cur.broken = false; } // look for voltage source loops // IES if ((ce instanceof VoltageElm && ce.getPostCount() == 2) /*|| ce instanceof WireElm*/) { FindPathInfo fpi = new FindPathInfo(FindPathInfo.VOLTAGE, ce, ce.getNode(1)); if (fpi.findPath(ce.getNode(0))) { stop(LS("Voltage source/wire loop with no resistance!"), ce); return; } } // look for shorted caps, or caps w/ voltage but no R if (ce instanceof CapacitorElm) { FindPathInfo fpi = new FindPathInfo(FindPathInfo.SHORT, ce, ce.getNode(1)); if (fpi.findPath(ce.getNode(0))) { console(ce + " shorted"); ce.reset(); } else { // a capacitor loop used to cause a matrix error. but we changed the capacitor model // so it works fine now. The only issue is if a capacitor is added in parallel with // another capacitor with a nonzero voltage; in that case we will get oscillation unless // we reset both capacitors to have the same voltage. Rather than check for that, we just // give an error. fpi = new FindPathInfo(FindPathInfo.CAP_V, ce, ce.getNode(1)); if (fpi.findPath(ce.getNode(0))) { stop(LS("Capacitor loop with no resistance!"), ce); return; } } } } //System.out.println("ac6"); simplifyMatrix(matrixSize); /* System.out.println("matrixSize = " + matrixSize + " " + circuitNonLinear); for (j = 0; j != circuitMatrixSize; j++) { for (i = 0; i != circuitMatrixSize; i++) System.out.print(circuitMatrix[j][i] + " "); System.out.print(" " + circuitRightSide[j] + "\n"); } System.out.print("\n");*/ // if a matrix is linear, we can do the lu_factor here instead of // needing to do it every frame if (!circuitNonLinear) { if (!lu_factor(circuitMatrix, circuitMatrixSize, circuitPermute)) { stop(LS("Singular matrix!"), null); return; } } // show resistance in voltage sources if there's only one boolean gotVoltageSource = false; showResistanceInVoltageSources = true; for (i = 0; i != elmList.size(); i++) { CircuitElm ce = getElm(i); if (ce instanceof VoltageElm) { if (gotVoltageSource) showResistanceInVoltageSources = false; else gotVoltageSource = true; } } } // simplify the matrix; this speeds things up quite a bit, especially for // digital circuits void simplifyMatrix(int matrixSize) { int i, j; for (i = 0; i != matrixSize; i++) { int qp = -1; double qv = 0; RowInfo re = circuitRowInfo[i]; /*System.out.println("row " + i + " " + re.lsChanges + " " + re.rsChanges + " " + re.dropRow);*/ if (re.lsChanges || re.dropRow || re.rsChanges) continue; double rsadd = 0; // look for rows that can be removed for (j = 0; j != matrixSize; j++) { double q = circuitMatrix[i][j]; if (circuitRowInfo[j].type == RowInfo.ROW_CONST) { // keep a running total of const values that have been // removed already rsadd -= circuitRowInfo[j].value*q; continue; } // ignore zeroes if (q == 0) continue; // keep track of first nonzero element that is not ROW_CONST if (qp == -1) { qp = j; qv = q; continue; } // more than one nonzero element? give up break; } if (j == matrixSize) { if (qp == -1) { stop(LS("Matrix error"), null); return; } RowInfo elt = circuitRowInfo[qp]; // we found a row with only one nonzero nonconst entry; that value // is a constant if (elt.type != RowInfo.ROW_NORMAL) { System.out.println("type already " + elt.type + " for " + qp + "!"); continue; } elt.type = RowInfo.ROW_CONST; // console("ROW_CONST " + i + " " + rsadd); elt.value = (circuitRightSide[i]+rsadd)/qv; circuitRowInfo[i].dropRow = true; i = -1; // start over from scratch } } //System.out.println("ac7"); // find size of new matrix int nn = 0; for (i = 0; i != matrixSize; i++) { RowInfo elt = circuitRowInfo[i]; if (elt.type == RowInfo.ROW_NORMAL) { elt.mapCol = nn++; //System.out.println("col " + i + " maps to " + elt.mapCol); continue; } if (elt.type == RowInfo.ROW_CONST) elt.mapCol = -1; } // make the new, simplified matrix int newsize = nn; double newmatx[][] = new double[newsize][newsize]; double newrs [] = new double[newsize]; int ii = 0; for (i = 0; i != matrixSize; i++) { RowInfo rri = circuitRowInfo[i]; if (rri.dropRow) { rri.mapRow = -1; continue; } newrs[ii] = circuitRightSide[i]; rri.mapRow = ii; //System.out.println("Row " + i + " maps to " + ii); for (j = 0; j != matrixSize; j++) { RowInfo ri = circuitRowInfo[j]; if (ri.type == RowInfo.ROW_CONST) newrs[ii] -= ri.value*circuitMatrix[i][j]; else newmatx[ii][ri.mapCol] += circuitMatrix[i][j]; } ii++; } // console("old size = " + matrixSize + " new size = " + newsize); circuitMatrix = newmatx; circuitRightSide = newrs; matrixSize = circuitMatrixSize = newsize; for (i = 0; i != matrixSize; i++) origRightSide[i] = circuitRightSide[i]; for (i = 0; i != matrixSize; i++) for (j = 0; j != matrixSize; j++) origMatrix[i][j] = circuitMatrix[i][j]; circuitNeedsMap = true; } // make list of posts we need to draw. posts shared by 2 elements should be hidden, all // others should be drawn. We can't use the node list anymore because wires have the same // node number at both ends. void makePostDrawList() { postDrawList = new Vector<Point>(); badConnectionList = new Vector<Point>(); for (Map.Entry<Point, Integer> entry : postCountMap.entrySet()) { if (entry.getValue() != 2) postDrawList.add(entry.getKey()); // look for bad connections, posts not connected to other elements which intersect // other elements' bounding boxes if (entry.getValue() == 1) { int j; boolean bad = false; Point cn = entry.getKey(); for (j = 0; j != elmList.size() && !bad; j++) { CircuitElm ce = getElm(j); if ( ce instanceof GraphicElm ) continue; // does this post intersect elm's bounding box? if (!ce.boundingBox.contains(cn.x, cn.y)) continue; int k; // does this post belong to the elm? int pc = ce.getPostCount(); for (k = 0; k != pc; k++) if (ce.getPost(k).equals(cn)) break; if (k == pc) bad = true; } if (bad) badConnectionList.add(cn); } } postCountMap = null; } class FindPathInfo { static final int INDUCT = 1; static final int VOLTAGE = 2; static final int SHORT = 3; static final int CAP_V = 4; boolean used[]; int dest; CircuitElm firstElm; int type; FindPathInfo(int t, CircuitElm e, int d) { dest = d; type = t; firstElm = e; used = new boolean[nodeList.size()]; } boolean findPath(int n1) { return findPath(n1, -1); } boolean findPath(int n1, int depth) { if (n1 == dest) return true; if (depth-- == 0) return false; if (used[n1]) { //System.out.println("used " + n1); return false; } used[n1] = true; int i; for (i = 0; i != elmList.size(); i++) { CircuitElm ce = getElm(i); if (ce == firstElm) continue; if (type == INDUCT) { // inductors need a path free of current sources if (ce instanceof CurrentElm) continue; } if (type == VOLTAGE) { // when checking for voltage loops, we only care about voltage sources/wires if (!(ce.isWire() || ce instanceof VoltageElm)) continue; } // when checking for shorts, just check wires if (type == SHORT && !ce.isWire()) continue; if (type == CAP_V) { // checking for capacitor/voltage source loops if (!(ce.isWire() || ce instanceof CapacitorElm || ce instanceof VoltageElm)) continue; } if (n1 == 0) { // look for posts which have a ground connection; // our path can go through ground int j; for (j = 0; j != ce.getConnectionNodeCount(); j++) if (ce.hasGroundConnection(j) && findPath(ce.getConnectionNode(j), depth)) { used[n1] = false; return true; } } int j; for (j = 0; j != ce.getConnectionNodeCount(); j++) { //System.out.println(ce + " " + ce.getNode(j)); if (ce.getConnectionNode(j) == n1) break; } if (j == ce.getConnectionNodeCount()) continue; if (ce.hasGroundConnection(j) && findPath(0, depth)) { //System.out.println(ce + " has ground"); used[n1] = false; return true; } if (type == INDUCT && ce instanceof InductorElm) { // inductors can use paths with other inductors of matching current double c = ce.getCurrent(); if (j == 0) c = -c; //System.out.println("matching " + c + " to " + firstElm.getCurrent()); //System.out.println(ce + " " + firstElm); if (Math.abs(c-firstElm.getCurrent()) > 1e-10) continue; } int k; for (k = 0; k != ce.getConnectionNodeCount(); k++) { if (j == k) continue; // console(ce + " " + ce.getNode(j) + "-" + ce.getNode(k)); if (ce.getConnection(j, k) && findPath(ce.getConnectionNode(k), depth)) { //System.out.println("got findpath " + n1); used[n1] = false; return true; } //System.out.println("back on findpath " + n1); } } used[n1] = false; //System.out.println(n1 + " failed"); return false; } } void stop(String s, CircuitElm ce) { stopMessage = s; circuitMatrix = null; // causes an exception stopElm = ce; setSimRunning(false); analyzeFlag = false; // cv.repaint(); } // control voltage source vs with voltage from n1 to n2 (must // also call stampVoltageSource()) void stampVCVS(int n1, int n2, double coef, int vs) { int vn = nodeList.size()+vs; stampMatrix(vn, n1, coef); stampMatrix(vn, n2, -coef); } // stamp independent voltage source #vs, from n1 to n2, amount v void stampVoltageSource(int n1, int n2, int vs, double v) { int vn = nodeList.size()+vs; stampMatrix(vn, n1, -1); stampMatrix(vn, n2, 1); stampRightSide(vn, v); stampMatrix(n1, vn, 1); stampMatrix(n2, vn, -1); } // use this if the amount of voltage is going to be updated in doStep() void stampVoltageSource(int n1, int n2, int vs) { int vn = nodeList.size()+vs; stampMatrix(vn, n1, -1); stampMatrix(vn, n2, 1); stampRightSide(vn); stampMatrix(n1, vn, 1); stampMatrix(n2, vn, -1); } void updateVoltageSource(int n1, int n2, int vs, double v) { int vn = nodeList.size()+vs; stampRightSide(vn, v); } void stampResistor(int n1, int n2, double r) { double r0 = 1/r; if (Double.isNaN(r0) || Double.isInfinite(r0)) { System.out.print("bad resistance " + r + " " + r0 + "\n"); int a = 0; a /= a; } stampMatrix(n1, n1, r0); stampMatrix(n2, n2, r0); stampMatrix(n1, n2, -r0); stampMatrix(n2, n1, -r0); } void stampConductance(int n1, int n2, double r0) { stampMatrix(n1, n1, r0); stampMatrix(n2, n2, r0); stampMatrix(n1, n2, -r0); stampMatrix(n2, n1, -r0); } // current from cn1 to cn2 is equal to voltage from vn1 to 2, divided by g void stampVCCurrentSource(int cn1, int cn2, int vn1, int vn2, double g) { stampMatrix(cn1, vn1, g); stampMatrix(cn2, vn2, g); stampMatrix(cn1, vn2, -g); stampMatrix(cn2, vn1, -g); } void stampCurrentSource(int n1, int n2, double i) { stampRightSide(n1, -i); stampRightSide(n2, i); } // stamp a current source from n1 to n2 depending on current through vs void stampCCCS(int n1, int n2, int vs, double gain) { int vn = nodeList.size()+vs; stampMatrix(n1, vn, gain); stampMatrix(n2, vn, -gain); } // stamp value x in row i, column j, meaning that a voltage change // of dv in node j will increase the current into node i by x dv. // (Unless i or j is a voltage source node.) void stampMatrix(int i, int j, double x) { if (i > 0 && j > 0) { if (circuitNeedsMap) { i = circuitRowInfo[i-1].mapRow; RowInfo ri = circuitRowInfo[j-1]; if (ri.type == RowInfo.ROW_CONST) { //System.out.println("Stamping constant " + i + " " + j + " " + x); circuitRightSide[i] -= x*ri.value; return; } j = ri.mapCol; //System.out.println("stamping " + i + " " + j + " " + x); } else { i--; j--; } circuitMatrix[i][j] += x; } } // stamp value x on the right side of row i, representing an // independent current source flowing into node i void stampRightSide(int i, double x) { if (i > 0) { if (circuitNeedsMap) { i = circuitRowInfo[i-1].mapRow; //System.out.println("stamping " + i + " " + x); } else i--; circuitRightSide[i] += x; } } // indicate that the value on the right side of row i changes in doStep() void stampRightSide(int i) { //System.out.println("rschanges true " + (i-1)); if (i > 0) circuitRowInfo[i-1].rsChanges = true; } // indicate that the values on the left side of row i change in doStep() void stampNonLinear(int i) { if (i > 0) circuitRowInfo[i-1].lsChanges = true; } double getIterCount() { // IES - remove interaction if (speedBar.getValue() == 0) return 0; return .1*Math.exp((speedBar.getValue()-61)/24.); } // we need to calculate wire currents for every iteration if someone is viewing a wire in the // scope. Otherwise we can do it only once per frame. boolean canDelayWireProcessing() { int i; for (i = 0; i != scopeCount; i++) if (scopes[i].getElm() instanceof WireElm) return false; return true; } boolean converged; int subIterations; void runCircuit(boolean didAnalyze) { if (circuitMatrix == null || elmList.size() == 0) { circuitMatrix = null; return; } int iter; //int maxIter = getIterCount(); boolean debugprint = dumpMatrix; dumpMatrix = false; long steprate = (long) (160*getIterCount()); long tm = System.currentTimeMillis(); long lit = lastIterTime; if (lit == 0) { lastIterTime = tm; return; } // Check if we don't need to run simulation (for very slow simulation speeds). // If the circuit changed, do at least one iteration to make sure everything is consistent. if (1000 >= steprate*(tm-lastIterTime) && !didAnalyze) return; boolean delayWireProcessing = canDelayWireProcessing(); for (iter = 1; ; iter++) { int i, j, k, subiter; for (i = 0; i != elmList.size(); i++) { CircuitElm ce = getElm(i); ce.startIteration(); } steps++; final int subiterCount = 5000; for (subiter = 0; subiter != subiterCount; subiter++) { converged = true; subIterations = subiter; for (i = 0; i != circuitMatrixSize; i++) circuitRightSide[i] = origRightSide[i]; if (circuitNonLinear) { for (i = 0; i != circuitMatrixSize; i++) for (j = 0; j != circuitMatrixSize; j++) circuitMatrix[i][j] = origMatrix[i][j]; } for (i = 0; i != elmList.size(); i++) { CircuitElm ce = getElm(i); ce.doStep(); } if (stopMessage != null) return; boolean printit = debugprint; debugprint = false; for (j = 0; j != circuitMatrixSize; j++) { for (i = 0; i != circuitMatrixSize; i++) { double x = circuitMatrix[i][j]; if (Double.isNaN(x) || Double.isInfinite(x)) { stop(LS("nan/infinite matrix!"), null); return; } } } if (printit) { for (j = 0; j != circuitMatrixSize; j++) { String x = ""; for (i = 0; i != circuitMatrixSize; i++) x += circuitMatrix[j][i] + ","; x += "\n"; console(x); } console(""); } if (circuitNonLinear) { if (converged && subiter > 0) break; if (!lu_factor(circuitMatrix, circuitMatrixSize, circuitPermute)) { stop(LS("Singular matrix!"), null); return; } } lu_solve(circuitMatrix, circuitMatrixSize, circuitPermute, circuitRightSide); for (j = 0; j != circuitMatrixFullSize; j++) { RowInfo ri = circuitRowInfo[j]; double res = 0; if (ri.type == RowInfo.ROW_CONST) res = ri.value; else res = circuitRightSide[ri.mapCol]; /*System.out.println(j + " " + res + " " + ri.type + " " + ri.mapCol);*/ if (Double.isNaN(res)) { converged = false; //debugprint = true; break; } if (j < nodeList.size()-1) { CircuitNode cn = getCircuitNode(j+1); for (k = 0; k != cn.links.size(); k++) { CircuitNodeLink cnl = (CircuitNodeLink) cn.links.elementAt(k); cnl.elm.setNodeVoltage(cnl.num, res); } } else { int ji = j-(nodeList.size()-1); //System.out.println("setting vsrc " + ji + " to " + res); voltageSources[ji].setCurrent(ji, res); } } if (!circuitNonLinear) break; } if (subiter > 5) console("converged after " + subiter + " iterations\n"); if (subiter == subiterCount) { stop(LS("Convergence failed!"), null); break; } t += timeStep; for (i = 0; i != elmList.size(); i++) { CircuitElm ce = getElm(i); ce.stepFinished(); } if (!delayWireProcessing) calcWireCurrents(); for (i = 0; i != scopeCount; i++) scopes[i].timeStep(); tm = System.currentTimeMillis(); lit = tm; if (iter*1000 >= steprate*(tm-lastIterTime) || (tm-lastFrameTime > 500)) break; } // for (iter = 1; ; iter++) lastIterTime = lit; if (delayWireProcessing) calcWireCurrents(); // System.out.println((System.currentTimeMillis()-lastFrameTime)/(double) iter); } // we removed wires from the matrix to speed things up. in order to display wire currents, // we need to calculate them now. void calcWireCurrents() { int i; // for debugging //for (i = 0; i != wireInfoList.size(); i++) // wireInfoList.get(i).wire.setCurrent(-1, 1.23); for (i = 0; i != wireInfoList.size(); i++) { WireInfo wi = wireInfoList.get(i); double cur = 0; int j; Point p = wi.wire.getPost(wi.post); for (j = 0; j != wi.neighbors.size(); j++) { CircuitElm ce = wi.neighbors.get(j); cur += ce.getCurrentIntoPoint(p.x, p.y); } if (wi.post == 0) wi.wire.setCurrent(-1, cur); else wi.wire.setCurrent(-1, -cur); } } int min(int a, int b) { return (a < b) ? a : b; } int max(int a, int b) { return (a > b) ? a : b; } public void resetAction(){ int i; for (i = 0; i != elmList.size(); i++) getElm(i).reset(); for (i = 0; i != scopeCount; i++) scopes[i].resetGraph(); // TODO: Will need to do IE bug fix here? analyzeFlag = true; t=0; setSimRunning(true); } public void menuPerformed(String menu, String item) { if (item=="about") aboutBox = new AboutBox(circuitjs1.versionString); if (item=="importfromlocalfile") { pushUndo(); loadFileInput.click(); } if (item=="importfromtext") { importFromTextDialog = new ImportFromTextDialog(this); } if (item=="importfromdropbox") { importFromDropboxDialog = new ImportFromDropboxDialog(this); } if (item=="exportasurl") { doExportAsUrl(); } if (item=="exportaslocalfile") doExportAsLocalFile(); if (item=="exportastext") doExportAsText(); if (item=="exporttodropbox") doExportToDropbox(); if ((menu=="elm" || menu=="scopepop") && contextPanel!=null) contextPanel.hide(); if (menu=="options" && item=="other") doEdit(new EditOptions(this)); if (item=="undo") doUndo(); if (item=="redo") doRedo(); if (item == "cut") { if (menu!="elm") menuElm = null; doCut(); } if (item == "copy") { if (menu!="elm") menuElm = null; doCopy(); } if (item=="paste") doPaste(null); if (item=="duplicate") { if (menu!="elm") menuElm = null; doDuplicate(); } if (item=="flip") doFlip(); if (item=="selectAll") doSelectAll(); // if (e.getSource() == exitItem) { // destroyFrame(); // return; // } if (item=="centrecircuit") { pushUndo(); centreCircuit(); } if (item=="stackAll") stackAll(); if (item=="unstackAll") unstackAll(); if (item=="combineAll") combineAll(); if (item=="zoomin") zoomCircuit(20); if (item=="zoomout") zoomCircuit(-20); if (item=="zoom100") setCircuitScale(1); if (menu=="elm" && item=="edit") doEdit(menuElm); if (item=="delete") { if (menu=="elm") menuElm = null; doDelete(); } if (item=="viewInScope" && menuElm != null) { int i; for (i = 0; i != scopeCount; i++) if (scopes[i].getElm() == null) break; if (i == scopeCount) { if (scopeCount == scopes.length) return; scopeCount++; scopes[i] = new Scope(this); scopes[i].position = i; //handleResize(); } scopes[i].setElm(menuElm); if (i > 0) scopes[i].speed = scopes[i-1].speed; } if (menu=="scopepop") { pushUndo(); if (item=="remove") scopes[menuScope].setElm(null); if (item=="removeplot") scopes[menuScope].removePlot(menuPlot); if (item=="speed2") scopes[menuScope].speedUp(); if (item=="speed1/2") scopes[menuScope].slowDown(); // if (item=="scale") // scopes[menuScope].adjustScale(.5); if (item=="maxscale") scopes[menuScope].maxScale(); if (item=="stack") stackScope(menuScope); if (item=="unstack") unstackScope(menuScope); if (item=="combine") combineScope(menuScope); if (item=="selecty") scopes[menuScope].selectY(); if (item=="reset") scopes[menuScope].resetGraph(); if (item.indexOf("show")==0 || item=="plotxy" || item=="showfft") { scopes[menuScope].handleMenu(item); } //cv.repaint(); } if (menu=="circuits" && item.indexOf("setup ") ==0) { pushUndo(); int sp = item.indexOf(' ', 6); readSetupFile(item.substring(6, sp), item.substring(sp+1), true); } // if (ac.indexOf("setup ") == 0) { // pushUndo(); // readSetupFile(ac.substring(6), // ((MenuItem) e.getSource()).getLabel()); // } // IES: Moved from itemStateChanged() if (menu=="main") { if (contextPanel!=null) contextPanel.hide(); // MenuItem mmi = (MenuItem) mi; // int prevMouseMode = mouseMode; setMouseMode(MODE_ADD_ELM); String s = item; if (s.length() > 0) mouseModeStr = s; if (s.compareTo("DragAll") == 0) setMouseMode(MODE_DRAG_ALL); else if (s.compareTo("DragRow") == 0) setMouseMode(MODE_DRAG_ROW); else if (s.compareTo("DragColumn") == 0) setMouseMode(MODE_DRAG_COLUMN); else if (s.compareTo("DragSelected") == 0) setMouseMode(MODE_DRAG_SELECTED); else if (s.compareTo("DragPost") == 0) setMouseMode(MODE_DRAG_POST); else if (s.compareTo("Select") == 0) setMouseMode(MODE_SELECT); // else if (s.length() > 0) { // try { // addingClass = Class.forName(s); // } catch (Exception ee) { // ee.printStackTrace(); // } // } // else // setMouseMode(prevMouseMode); tempMouseMode = mouseMode; } } void stackScope(int s) { if (s == 0) { if (scopeCount < 2) return; s = 1; } if (scopes[s].position == scopes[s-1].position) return; scopes[s].position = scopes[s-1].position; for (s++; s < scopeCount; s++) scopes[s].position--; } void unstackScope(int s) { if (s == 0) { if (scopeCount < 2) return; s = 1; } if (scopes[s].position != scopes[s-1].position) return; for (; s < scopeCount; s++) scopes[s].position++; } void combineScope(int s) { if (s == 0) { if (scopeCount < 2) return; s = 1; } scopes[s-1].combine(scopes[s]); scopes[s].setElm(null); } void stackAll() { int i; for (i = 0; i != scopeCount; i++) { scopes[i].position = 0; scopes[i].showMax = scopes[i].showMin = false; } } void unstackAll() { int i; for (i = 0; i != scopeCount; i++) { scopes[i].position = i; scopes[i].showMax = true; } } void combineAll() { int i; for (i = scopeCount-2; i >= 0; i--) { scopes[i].combine(scopes[i+1]); scopes[i+1].setElm(null); } } void doEdit(Editable eable) { clearSelection(); pushUndo(); if (editDialog != null) { // requestFocus(); editDialog.setVisible(false); editDialog = null; } editDialog = new EditDialog(eable, this); editDialog.show(); } void doExportAsUrl() { String dump = dumpCircuit(); // if (expDialog == null) { // expDialog = ImportExportDialogFactory.Create(this, // ImportExportDialog.Action.EXPORT); //// expDialog = new ImportExportClipboardDialog(this, //// ImportExportDialog.Action.EXPORT); // } // expDialog.setDump(dump); // expDialog.execute(); exportAsUrlDialog = new ExportAsUrlDialog(dump); exportAsUrlDialog.show(); } void doExportAsText() { String dump = dumpCircuit(); exportAsTextDialog = new ExportAsTextDialog(this, dump); exportAsTextDialog.show(); } void doExportAsLocalFile() { String dump = dumpCircuit(); exportAsLocalFileDialog = new ExportAsLocalFileDialog(dump); exportAsLocalFileDialog.show(); } void doExportToDropbox() { String dump = dumpCircuit(); exportToDropbox = new ExportToDropbox(dump); } String dumpCircuit() { int i; CustomLogicModel.clearDumpedFlags(); int f = (dotsCheckItem.getState()) ? 1 : 0; f |= (smallGridCheckItem.getState()) ? 2 : 0; f |= (voltsCheckItem.getState()) ? 0 : 4; f |= (powerCheckItem.getState()) ? 8 : 0; f |= (showValuesCheckItem.getState()) ? 0 : 16; // 32 = linear scale in afilter String dump = "$ " + f + " " + timeStep + " " + getIterCount() + " " + currentBar.getValue() + " " + CircuitElm.voltageRange + " " + powerBar.getValue() + "\n"; for (i = 0; i != elmList.size(); i++) { CircuitElm ce = getElm(i); if (ce instanceof CustomLogicElm) { String m = ((CustomLogicElm)ce).dumpModel(); if (!m.isEmpty()) dump += m + "\n"; } dump += ce.dump() + "\n"; } for (i = 0; i != scopeCount; i++) { String d = scopes[i].dump(); if (d != null) dump += d + "\n"; } if (hintType != -1) dump += "h " + hintType + " " + hintItem1 + " " + hintItem2 + "\n"; return dump; } void getSetupList(final boolean openDefault) { String url; url = GWT.getModuleBaseURL()+"setuplist.txt"+"?v="+random.nextInt(); RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.GET, url); try { requestBuilder.sendRequest(null, new RequestCallback() { public void onError(Request request, Throwable exception) { GWT.log("File Error Response", exception); } public void onResponseReceived(Request request, Response response) { // processing goes here if (response.getStatusCode()==Response.SC_OK) { String text = response.getText(); processSetupList(text.getBytes(), text.length(), openDefault); // end or processing } else GWT.log("Bad file server response:"+response.getStatusText() ); } }); } catch (RequestException e) { GWT.log("failed file reading", e); } } void processSetupList(byte b[], int len, final boolean openDefault) { MenuBar currentMenuBar; MenuBar stack[] = new MenuBar[6]; int stackptr = 0; currentMenuBar=new MenuBar(true); currentMenuBar.setAutoOpen(true); menuBar.addItem(LS("Circuits"), currentMenuBar); stack[stackptr++] = currentMenuBar; int p; for (p = 0; p < len; ) { int l; for (l = 0; l != len-p; l++) if (b[l+p] == '\n') { l++; break; } String line = new String(b, p, l-1); if (line.charAt(0) == '#') ; else if (line.charAt(0) == '+') { // MenuBar n = new Menu(line.substring(1)); MenuBar n = new MenuBar(true); n.setAutoOpen(true); currentMenuBar.addItem(LS(line.substring(1)),n); currentMenuBar = stack[stackptr++] = n; } else if (line.charAt(0) == '-') { currentMenuBar = stack[--stackptr-1]; } else { int i = line.indexOf(' '); if (i > 0) { String title = LS(line.substring(i+1)); boolean first = false; if (line.charAt(0) == '>') first = true; String file = line.substring(first ? 1 : 0, i); currentMenuBar.addItem(new MenuItem(title, new MyCommand("circuits", "setup "+file+" " + title))); if (file.equals(startCircuit) && startLabel == null) { startLabel = title; titleLabel.setText(title); } if (first && startCircuit == null) { startCircuit = file; startLabel = title; if (openDefault && stopMessage == null) readSetupFile(startCircuit, startLabel, true); } } } p += l; } } void readSetup(String text, boolean centre) { readSetup(text, false, centre); titleLabel.setText(null); } void readSetup(String text, boolean retain, boolean centre) { readSetup(text.getBytes(), text.length(), retain, centre); titleLabel.setText(null); } void readSetupFile(String str, String title, boolean centre) { t = 0; System.out.println(str); // TODO: Maybe think about some better approach to cache management! String url=GWT.getModuleBaseURL()+"circuits/"+str+"?v="+random.nextInt(); loadFileFromURL(url, centre); if (title != null) titleLabel.setText(title); } void loadFileFromURL(String url, final boolean centre) { RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.GET, url); try { requestBuilder.sendRequest(null, new RequestCallback() { public void onError(Request request, Throwable exception) { GWT.log("File Error Response", exception); } public void onResponseReceived(Request request, Response response) { if (response.getStatusCode()==Response.SC_OK) { String text = response.getText(); readSetup(text.getBytes(), text.length(), false, centre); } else GWT.log("Bad file server response:"+response.getStatusText() ); } }); } catch (RequestException e) { GWT.log("failed file reading", e); } } void readSetup(byte b[], int len, boolean retain, boolean centre) { int i; if (!retain) { clearMouseElm(); for (i = 0; i != elmList.size(); i++) { CircuitElm ce = getElm(i); ce.delete(); } elmList.removeAllElements(); hintType = -1; timeStep = 5e-6; dotsCheckItem.setState(false); smallGridCheckItem.setState(false); powerCheckItem.setState(false); voltsCheckItem.setState(true); showValuesCheckItem.setState(true); setGrid(); speedBar.setValue(117); // 57 currentBar.setValue(50); powerBar.setValue(50); CircuitElm.voltageRange = 5; scopeCount = 0; lastIterTime = 0; } //cv.repaint(); int p; for (p = 0; p < len; ) { int l; int linelen = len-p; // IES - changed to allow the last line to not end with a delim. for (l = 0; l != len-p; l++) if (b[l+p] == '\n' || b[l+p] == '\r') { linelen = l++; if (l+p < b.length && b[l+p] == '\n') l++; break; } String line = new String(b, p, linelen); StringTokenizer st = new StringTokenizer(line, " +\t\n\r\f"); while (st.hasMoreTokens()) { String type = st.nextToken(); int tint = type.charAt(0); try { if (tint == 'o') { Scope sc = new Scope(this); sc.position = scopeCount; sc.undump(st); scopes[scopeCount++] = sc; break; } if (tint == 'h') { readHint(st); break; } if (tint == '$') { readOptions(st); break; } if (tint == '!') { new CustomLogicModel(st); break; } if (tint == '%' || tint == '?' || tint == 'B') { // ignore afilter-specific stuff break; } if (tint >= '0' && tint <= '9') tint = new Integer(type).intValue(); int x1 = new Integer(st.nextToken()).intValue(); int y1 = new Integer(st.nextToken()).intValue(); int x2 = new Integer(st.nextToken()).intValue(); int y2 = new Integer(st.nextToken()).intValue(); int f = new Integer(st.nextToken()).intValue(); // The following lines are functionally replaced by the call to // createCe below // Class cls = dumpTypes[tint]; // if (cls == null) { // System.out.println("unrecognized dump type: " + type); // break; // } // // find element class // Class carr[] = new Class[6]; // //carr[0] = getClass(); // carr[0] = carr[1] = carr[2] = carr[3] = carr[4] = // int.class; // carr[5] = StringTokenizer.class; // Constructor cstr = null; // cstr = cls.getConstructor(carr); // // // invoke constructor with starting coordinates // Object oarr[] = new Object[6]; // //oarr[0] = this; // oarr[0] = new Integer(x1); // oarr[1] = new Integer(y1); // oarr[2] = new Integer(x2); // oarr[3] = new Integer(y2); // oarr[4] = new Integer(f ); // oarr[5] = st; // ce = (CircuitElm) cstr.newInstance(oarr); CircuitElm newce = createCe(tint, x1, y1, x2, y2, f, st); if (newce==null) { System.out.println("unrecognized dump type: " + type); break; } newce.setPoints(); elmList.addElement(newce); // } catch (java.lang.reflect.InvocationTargetException ee) { // ee.getTargetException().printStackTrace(); // break; } catch (Exception ee) { ee.printStackTrace(); console("exception while undumping " + ee); break; } break; } p += l; } setPowerBarEnable(); enableItems(); // if (!retain) // handleResize(); // for scopes needAnalyze(); if (centre) centreCircuit(); } void readHint(StringTokenizer st) { hintType = new Integer(st.nextToken()).intValue(); hintItem1 = new Integer(st.nextToken()).intValue(); hintItem2 = new Integer(st.nextToken()).intValue(); } void readOptions(StringTokenizer st) { int flags = new Integer(st.nextToken()).intValue(); // IES - remove inteaction dotsCheckItem.setState((flags & 1) != 0); smallGridCheckItem.setState((flags & 2) != 0); voltsCheckItem.setState((flags & 4) == 0); powerCheckItem.setState((flags & 8) == 8); showValuesCheckItem.setState((flags & 16) == 0); timeStep = new Double (st.nextToken()).doubleValue(); double sp = new Double(st.nextToken()).doubleValue(); int sp2 = (int) (Math.log(10*sp)*24+61.5); //int sp2 = (int) (Math.log(sp)*24+1.5); speedBar.setValue(sp2); currentBar.setValue(new Integer(st.nextToken()).intValue()); CircuitElm.voltageRange = new Double (st.nextToken()).doubleValue(); try { powerBar.setValue(new Integer(st.nextToken()).intValue()); } catch (Exception e) { } setGrid(); } int snapGrid(int x) { return (x+gridRound) & gridMask; } boolean doSwitch(int x, int y) { if (mouseElm == null || !(mouseElm instanceof SwitchElm)) return false; SwitchElm se = (SwitchElm) mouseElm; if (!se.getSwitchRect().contains(x, y)) return false; se.toggle(); if (se.momentary) heldSwitchElm = se; needAnalyze(); return true; } int locateElm(CircuitElm elm) { int i; for (i = 0; i != elmList.size(); i++) if (elm == elmList.elementAt(i)) return i; return -1; } public void mouseDragged(MouseMoveEvent e) { // ignore right mouse button with no modifiers (needed on PC) if (e.getNativeButton()==NativeEvent.BUTTON_RIGHT) { if (!(e.isMetaKeyDown() || e.isShiftKeyDown() || e.isControlKeyDown() || e.isAltKeyDown())) return; } if (tempMouseMode==MODE_DRAG_SPLITTER) { dragSplitter(e.getX(), e.getY()); return; } int gx = inverseTransformX(e.getX()); int gy = inverseTransformY(e.getY()); if (!circuitArea.contains(e.getX(), e.getY())) return; if (dragElm != null) dragElm.drag(gx, gy); boolean success = true; switch (tempMouseMode) { case MODE_DRAG_ALL: dragAll(e.getX(), e.getY()); break; case MODE_DRAG_ROW: dragRow(snapGrid(gx), snapGrid(gy)); break; case MODE_DRAG_COLUMN: dragColumn(snapGrid(gx), snapGrid(gy)); break; case MODE_DRAG_POST: if (mouseElm != null) dragPost(snapGrid(gx), snapGrid(gy)); break; case MODE_SELECT: if (mouseElm == null) selectArea(gx, gy); else { // wait short delay before dragging. This is to fix problem where switches were accidentally getting // dragged when tapped on mobile devices if (System.currentTimeMillis()-mouseDownTime < 150) return; tempMouseMode = MODE_DRAG_SELECTED; success = dragSelected(gx, gy); } break; case MODE_DRAG_SELECTED: success = dragSelected(gx, gy); break; } dragging = true; if (success) { dragScreenX = e.getX(); dragScreenY = e.getY(); // console("setting dragGridx in mousedragged"); dragGridX = inverseTransformX(dragScreenX); dragGridY = inverseTransformY(dragScreenY); if (!(tempMouseMode == MODE_DRAG_SELECTED && onlyGraphicsElmsSelected())) { dragGridX = snapGrid(dragGridX); dragGridY = snapGrid(dragGridY); } } } void dragSplitter(int x, int y) { double h = (double) cv.getCanvasElement().getHeight(); if (h<1) h=1; scopeHeightFraction=1.0-(((double)y)/h); if (scopeHeightFraction<0.1) scopeHeightFraction=0.1; if (scopeHeightFraction>0.9) scopeHeightFraction=0.9; setCircuitArea(); } void dragAll(int x, int y) { int dx = x-dragScreenX; int dy = y-dragScreenY; if (dx == 0 && dy == 0) return; transform[4] += dx; transform[5] += dy; dragScreenX = x; dragScreenY = y; } void dragRow(int x, int y) { int dy = y-dragGridY; if (dy == 0) return; int i; for (i = 0; i != elmList.size(); i++) { CircuitElm ce = getElm(i); if (ce.y == dragGridY) ce.movePoint(0, 0, dy); if (ce.y2 == dragGridY) ce.movePoint(1, 0, dy); } removeZeroLengthElements(); } void dragColumn(int x, int y) { int dx = x-dragGridX; if (dx == 0) return; int i; for (i = 0; i != elmList.size(); i++) { CircuitElm ce = getElm(i); if (ce.x == dragGridX) ce.movePoint(0, dx, 0); if (ce.x2 == dragGridX) ce.movePoint(1, dx, 0); } removeZeroLengthElements(); } boolean onlyGraphicsElmsSelected() { if (mouseElm!=null && !(mouseElm instanceof GraphicElm)) return false; int i; for (i = 0; i != elmList.size(); i++) { CircuitElm ce = getElm(i); if ( ce.isSelected() && !(ce instanceof GraphicElm) ) return false; } return true; } boolean dragSelected(int x, int y) { boolean me = false; int i; if (mouseElm != null && !mouseElm.isSelected()) mouseElm.setSelected(me = true); if (! onlyGraphicsElmsSelected()) { // console("Snapping x and y"); x = snapGrid(x); y = snapGrid(y); } int dx = x-dragGridX; // console("dx="+dx+"dragGridx="+dragGridX); int dy = y-dragGridY; if (dx == 0 && dy == 0) { // don't leave mouseElm selected if we selected it above if (me) mouseElm.setSelected(false); return false; } boolean allowed = true; // check if moves are allowed for (i = 0; allowed && i != elmList.size(); i++) { CircuitElm ce = getElm(i); if (ce.isSelected() && !ce.allowMove(dx, dy)) allowed = false; } if (allowed) { for (i = 0; i != elmList.size(); i++) { CircuitElm ce = getElm(i); if (ce.isSelected()) ce.move(dx, dy); } needAnalyze(); } // don't leave mouseElm selected if we selected it above if (me) mouseElm.setSelected(false); return allowed; } void dragPost(int x, int y) { if (draggingPost == -1) { draggingPost = (Graphics.distanceSq(mouseElm.x , mouseElm.y , x, y) > Graphics.distanceSq(mouseElm.x2, mouseElm.y2, x, y)) ? 1 : 0; } int dx = x-dragGridX; int dy = y-dragGridY; if (dx == 0 && dy == 0) return; mouseElm.movePoint(draggingPost, dx, dy); needAnalyze(); } void doFlip() { mouseElm.flipPosts(); needAnalyze(); } void selectArea(int x, int y) { int x1 = min(x, initDragGridX); int x2 = max(x, initDragGridX); int y1 = min(y, initDragGridY); int y2 = max(y, initDragGridY); selectedArea = new Rectangle(x1, y1, x2-x1, y2-y1); int i; for (i = 0; i != elmList.size(); i++) { CircuitElm ce = getElm(i); ce.selectRect(selectedArea); } } // void setSelectedElm(CircuitElm cs) { // int i; // for (i = 0; i != elmList.size(); i++) { // CircuitElm ce = getElm(i); // ce.setSelected(ce == cs); // } // mouseElm = cs; // } void setMouseElm(CircuitElm ce) { if (ce!=mouseElm) { if (mouseElm!=null) mouseElm.setMouseElm(false); if (ce!=null) ce.setMouseElm(true); mouseElm=ce; } } void removeZeroLengthElements() { int i; boolean changed = false; for (i = elmList.size()-1; i >= 0; i--) { CircuitElm ce = getElm(i); if (ce.x == ce.x2 && ce.y == ce.y2) { elmList.removeElementAt(i); ce.delete(); changed = true; } } needAnalyze(); } boolean mouseIsOverSplitter(int x, int y) { boolean isOverSplitter; isOverSplitter =((x>=0) && (x<circuitArea.width) && (y>=circuitArea.height-5) && (y<circuitArea.height)); if (isOverSplitter!=mouseWasOverSplitter){ if (isOverSplitter) setCursorStyle("cursorSplitter"); else setMouseMode(mouseMode); } mouseWasOverSplitter=isOverSplitter; return isOverSplitter; } public void onMouseMove(MouseMoveEvent e) { e.preventDefault(); mouseCursorX=e.getX(); mouseCursorY=e.getY(); if (mouseDragging) { mouseDragged(e); return; } mouseSelect(e); } // convert screen coordinates to grid coordinates by inverting circuit transform int inverseTransformX(double x) { return (int) ((x-transform[4])/transform[0]); } int inverseTransformY(double y) { return (int) ((y-transform[5])/transform[3]); } // need to break this out into a separate routine to handle selection, // since we don't get mouse move events on mobile public void mouseSelect(MouseEvent<?> e) { // The following is in the original, but seems not to work/be needed for GWT // if (e.getNativeButton()==NativeEvent.BUTTON_LEFT) // return; CircuitElm newMouseElm=null; mouseCursorX=e.getX(); mouseCursorY=e.getY(); int sx = e.getX(); int sy = e.getY(); int gx = inverseTransformX(sx); int gy = inverseTransformY(sy); // console("Settingd draggridx in mouseEvent"); dragGridX = snapGrid(gx); dragGridY = snapGrid(gy); dragScreenX = sx; dragScreenY = sy; draggingPost = -1; int i; // CircuitElm origMouse = mouseElm; mousePost = -1; plotXElm = plotYElm = null; if (mouseIsOverSplitter(sx, sy)) { setMouseElm(null); return; } if (mouseElm!=null && ( mouseElm.getHandleGrabbedClose(gx, gy, POSTGRABSQ, MINPOSTGRABSIZE)>=0)) { newMouseElm=mouseElm; } else { int bestDist = 100000000; int bestArea = 100000000; for (i = 0; i != elmList.size(); i++) { CircuitElm ce = getElm(i); if (ce.boundingBox.contains(gx, gy)) { int j; int area = ce.boundingBox.width * ce.boundingBox.height; int jn = ce.getPostCount(); if (jn > 2) jn = 2; for (j = 0; j != jn; j++) { Point pt = ce.getPost(j); int dist = Graphics.distanceSq(gx, gy, pt.x, pt.y); // if multiple elements have overlapping bounding boxes, // we prefer selecting elements that have posts close // to the mouse pointer and that have a small bounding // box area. if (dist <= bestDist && area <= bestArea) { bestDist = dist; bestArea = area; newMouseElm = ce; } } if (ce.getPostCount() == 0) newMouseElm = ce; } } // for } scopeSelected = -1; if (newMouseElm == null) { for (i = 0; i != scopeCount; i++) { Scope s = scopes[i]; if (s.rect.contains(sx, sy)) { newMouseElm=s.getElm(); if (s.plotXY) { plotXElm = s.getXElm(); plotYElm = s.getYElm(); } scopeSelected = i; } } // // the mouse pointer was not in any of the bounding boxes, but we // // might still be close to a post for (i = 0; i != elmList.size(); i++) { CircuitElm ce = getElm(i); if (mouseMode==MODE_DRAG_POST ) { if (ce.getHandleGrabbedClose(gx, gy, POSTGRABSQ, 0)> 0) { newMouseElm = ce; break; } } int j; int jn = ce.getPostCount(); for (j = 0; j != jn; j++) { Point pt = ce.getPost(j); // int dist = Graphics.distanceSq(x, y, pt.x, pt.y); if (Graphics.distanceSq(pt.x, pt.y, gx, gy) < 26) { newMouseElm = ce; mousePost = j; break; } } } } else { mousePost = -1; // look for post close to the mouse pointer for (i = 0; i != newMouseElm.getPostCount(); i++) { Point pt = newMouseElm.getPost(i); if (Graphics.distanceSq(pt.x, pt.y, gx, gy) < 26) mousePost = i; } } // if (mouseElm != origMouse) // cv.repaint(); setMouseElm(newMouseElm); } public void onContextMenu(ContextMenuEvent e) { e.preventDefault(); menuX = e.getNativeEvent().getClientX(); menuY = e.getNativeEvent().getClientY(); doPopupMenu(); } void doPopupMenu() { menuElm = mouseElm; menuScope=-1; menuPlot=-1; int x, y; if (scopeSelected!=-1) { MenuBar m=scopes[scopeSelected].getMenu(); menuScope=scopeSelected; menuPlot=scopes[scopeSelected].selectedPlot; if (m!=null) { contextPanel=new PopupPanel(true); contextPanel.add(m); y=Math.max(0, Math.min(menuY,cv.getCoordinateSpaceHeight()-430)); contextPanel.setPopupPosition(menuX, y); contextPanel.show(); } } else if (mouseElm != null) { elmScopeMenuItem.setEnabled(mouseElm.canViewInScope()); elmEditMenuItem .setEnabled(mouseElm.getEditInfo(0) != null); elmFlipMenuItem .setEnabled(mouseElm.getPostCount() == 2); contextPanel=new PopupPanel(true); contextPanel.add(elmMenuBar); contextPanel.setPopupPosition(menuX, menuY); contextPanel.show(); } else { doMainMenuChecks(); contextPanel=new PopupPanel(true); contextPanel.add(mainMenuBar); x=Math.max(0, Math.min(menuX, cv.getCoordinateSpaceWidth()-400)); y=Math.max(0, Math.min(menuY,cv.getCoordinateSpaceHeight()-450)); contextPanel.setPopupPosition(x,y); contextPanel.show(); } } void longPress() { doPopupMenu(); } // public void mouseClicked(MouseEvent e) { public void onClick(ClickEvent e) { e.preventDefault(); // //IES - remove inteaction //// if ( e.getClickCount() == 2 && !didSwitch ) //// doEditMenu(e); // if (e.getNativeButton() == NativeEvent.BUTTON_LEFT) { // if (mouseMode == MODE_SELECT || mouseMode == MODE_DRAG_SELECTED) // clearSelection(); // } if ((e.getNativeButton() == NativeEvent.BUTTON_MIDDLE)) scrollValues(e.getNativeEvent().getClientX(), e.getNativeEvent().getClientY(), 0); } public void onDoubleClick(DoubleClickEvent e){ e.preventDefault(); // if (!didSwitch && mouseElm != null) if (mouseElm != null && !(mouseElm instanceof SwitchElm)) doEdit(mouseElm); } // public void mouseEntered(MouseEvent e) { // } public void onMouseOut(MouseOutEvent e) { mouseCursorX=-1; } void clearMouseElm() { scopeSelected = -1; mouseElm = plotXElm = plotYElm = null; } int menuX, menuY; public void onMouseDown(MouseDownEvent e) { // public void mousePressed(MouseEvent e) { e.preventDefault(); menuX = e.getX(); menuY = e.getY(); mouseDownTime = System.currentTimeMillis(); // maybe someone did copy in another window? should really do this when // window receives focus enablePaste(); // IES - hack to only handle left button events in the web version. if (e.getNativeButton() != NativeEvent.BUTTON_LEFT) return; // set mouseElm in case we are on mobile mouseSelect(e); mouseDragging=true; didSwitch = false; if (mouseWasOverSplitter) { tempMouseMode = MODE_DRAG_SPLITTER; return; } if (e.getNativeButton() == NativeEvent.BUTTON_LEFT) { // // left mouse tempMouseMode = mouseMode; if (e.isAltKeyDown() && e.isMetaKeyDown()) tempMouseMode = MODE_DRAG_COLUMN; else if (e.isAltKeyDown() && e.isShiftKeyDown()) tempMouseMode = MODE_DRAG_ROW; else if (e.isShiftKeyDown()) tempMouseMode = MODE_SELECT; else if (e.isAltKeyDown()) tempMouseMode = MODE_DRAG_ALL; else if (e.isControlKeyDown() || e.isMetaKeyDown()) tempMouseMode = MODE_DRAG_POST; } int gx = inverseTransformX(e.getX()); int gy = inverseTransformY(e.getY()); if (doSwitch(gx, gy)) { // do this BEFORE we change the mouse mode to MODE_DRAG_POST! Or else logic inputs // will add dots to the whole circuit when we click on them! didSwitch = true; return; } // IES - Grab resize handles in select mode if they are far enough apart and you are on top of them if (tempMouseMode == MODE_SELECT && mouseElm!=null && mouseElm.getHandleGrabbedClose(gx, gy, POSTGRABSQ, MINPOSTGRABSIZE) >=0 && !anySelectedButMouse() ) tempMouseMode = MODE_DRAG_POST; if (tempMouseMode != MODE_SELECT && tempMouseMode != MODE_DRAG_SELECTED) clearSelection(); pushUndo(); initDragGridX = gx; initDragGridY = gy; dragging = true; if (tempMouseMode !=MODE_ADD_ELM) return; // int x0 = snapGrid(gx); int y0 = snapGrid(gy); if (!circuitArea.contains(e.getX(), e.getY())) return; dragElm = constructElement(mouseModeStr, x0, y0); } void doMainMenuChecks() { int c = mainMenuItems.size(); int i; for (i=0; i<c ; i++) mainMenuItems.get(i).setState(mainMenuItemNames.get(i)==mouseModeStr); } public void onMouseUp(MouseUpEvent e) { e.preventDefault(); mouseDragging=false; // click to clear selection if (tempMouseMode == MODE_SELECT && selectedArea == null) clearSelection(); tempMouseMode = mouseMode; selectedArea = null; dragging = false; boolean circuitChanged = false; if (heldSwitchElm != null) { heldSwitchElm.mouseUp(); heldSwitchElm = null; circuitChanged = true; } if (dragElm != null) { // if the element is zero size then don't create it // IES - and disable any previous selection if (dragElm.x == dragElm.x2 && dragElm.y == dragElm.y2) { dragElm.delete(); if (mouseMode == MODE_SELECT || mouseMode == MODE_DRAG_SELECTED) clearSelection(); } else { elmList.addElement(dragElm); dragElm.draggingDone(); circuitChanged = true; } dragElm = null; } if (circuitChanged) needAnalyze(); if (dragElm != null) dragElm.delete(); dragElm = null; // cv.repaint(); } public void onMouseWheel(MouseWheelEvent e) { e.preventDefault(); // once we start zooming, don't allow other uses of mouse wheel for a while // so we don't accidentally edit a resistor value while zooming boolean zoomOnly = System.currentTimeMillis() < zoomTime+1000; if (!zoomOnly) scrollValues(e.getNativeEvent().getClientX(), e.getNativeEvent().getClientY(), e.getDeltaY()); if (mouseElm instanceof MouseWheelHandler && !zoomOnly) ((MouseWheelHandler) mouseElm).onMouseWheel(e); else if (scopeSelected != -1) scopes[scopeSelected].onMouseWheel(e); else if (!dialogIsShowing()) { zoomCircuit(e.getDeltaY()); zoomTime = System.currentTimeMillis(); } } void zoomCircuit(int dy) { double newScale; double oldScale = transform[0]; double val = dy*.01; newScale = Math.max(oldScale+val, .2); newScale = Math.min(newScale, 2.5); setCircuitScale(newScale); } void setCircuitScale(double newScale) { int cx = inverseTransformX(circuitArea.width/2); int cy = inverseTransformY(circuitArea.height/2); transform[0] = transform[3] = newScale; // adjust translation to keep center of screen constant // inverse transform = (x-t4)/t0 transform[4] = circuitArea.width /2 - cx*newScale; transform[5] = circuitArea.height/2 - cy*newScale; } void setPowerBarEnable() { if (powerCheckItem.getState()) { powerLabel.setStyleName("disabled", false); powerBar.enable(); } else { powerLabel.setStyleName("disabled", true); powerBar.disable(); } } void scrollValues(int x, int y, int deltay) { if (mouseElm!=null && !dialogIsShowing() && scopeSelected == -1) if (mouseElm instanceof ResistorElm || mouseElm instanceof CapacitorElm || mouseElm instanceof InductorElm) { scrollValuePopup = new ScrollValuePopup(x, y, deltay, mouseElm, this); } } void enableItems() { } void setGrid() { gridSize = (smallGridCheckItem.getState()) ? 8 : 16; gridMask = ~(gridSize-1); gridRound = gridSize/2-1; } void pushUndo() { redoStack.removeAllElements(); String s = dumpCircuit(); if (undoStack.size() > 0 && s.compareTo(undoStack.lastElement()) == 0) return; undoStack.add(s); enableUndoRedo(); } void doUndo() { if (undoStack.size() == 0) return; redoStack.add(dumpCircuit()); String s = undoStack.remove(undoStack.size()-1); readSetup(s, false); enableUndoRedo(); } void doRedo() { if (redoStack.size() == 0) return; undoStack.add(dumpCircuit()); String s = redoStack.remove(redoStack.size()-1); readSetup(s, false); enableUndoRedo(); } void enableUndoRedo() { redoItem.setEnabled(redoStack.size() > 0); undoItem.setEnabled(undoStack.size() > 0); } void setMouseMode(int mode) { mouseMode = mode; if ( mode == MODE_ADD_ELM ) { setCursorStyle("cursorCross"); } else { setCursorStyle("cursorPointer"); } } void setCursorStyle(String s) { if (lastCursorStyle!=null) cv.removeStyleName(lastCursorStyle); cv.addStyleName(s); lastCursorStyle=s; } void setMenuSelection() { if (menuElm != null) { if (menuElm.selected) return; clearSelection(); menuElm.setSelected(true); } } void doCut() { int i; pushUndo(); setMenuSelection(); clipboard = ""; for (i = elmList.size()-1; i >= 0; i--) { CircuitElm ce = getElm(i); if (ce.isSelected()) { clipboard += ce.dump() + "\n"; ce.delete(); elmList.removeElementAt(i); } } writeClipboardToStorage(); enablePaste(); needAnalyze(); } void writeClipboardToStorage() { Storage stor = Storage.getLocalStorageIfSupported(); if (stor == null) return; stor.setItem("circuitClipboard", clipboard); } void readClipboardFromStorage() { Storage stor = Storage.getLocalStorageIfSupported(); if (stor == null) return; clipboard = stor.getItem("circuitClipboard"); } void doDelete() { int i; pushUndo(); setMenuSelection(); boolean hasDeleted = false; for (i = elmList.size()-1; i >= 0; i--) { CircuitElm ce = getElm(i); if (ce.isSelected()) { ce.delete(); elmList.removeElementAt(i); hasDeleted = true; } } if ( !hasDeleted ) { for (i = elmList.size()-1; i >= 0; i--) { CircuitElm ce = getElm(i); if (ce == mouseElm) { ce.delete(); elmList.removeElementAt(i); hasDeleted = true; setMouseElm(null); break; } } } if ( hasDeleted ) needAnalyze(); } void doCopy() { int i; clipboard = ""; setMenuSelection(); for (i = elmList.size()-1; i >= 0; i--) { CircuitElm ce = getElm(i); if (ce.isSelected()) clipboard += ce.dump() + "\n"; } writeClipboardToStorage(); enablePaste(); } void enablePaste() { if (clipboard == null || clipboard.length() == 0) readClipboardFromStorage(); pasteItem.setEnabled(clipboard != null && clipboard.length() > 0); } void doDuplicate() { int i; String s = ""; setMenuSelection(); for (i = elmList.size()-1; i >= 0; i--) { CircuitElm ce = getElm(i); if (ce.isSelected()) s += ce.dump() + "\n"; } doPaste(s); } void doPaste(String dump) { pushUndo(); clearSelection(); int i; Rectangle oldbb = null; for (i = 0; i != elmList.size(); i++) { CircuitElm ce = getElm(i); Rectangle bb = ce.getBoundingBox(); if (oldbb != null) oldbb = oldbb.union(bb); else oldbb = bb; } int oldsz = elmList.size(); if (dump != null) readSetup(dump, true, false); else { readClipboardFromStorage(); readSetup(clipboard, true, false); } // select new items Rectangle newbb = null; for (i = oldsz; i != elmList.size(); i++) { CircuitElm ce = getElm(i); ce.setSelected(true); Rectangle bb = ce.getBoundingBox(); if (newbb != null) newbb = newbb.union(bb); else newbb = bb; } if (oldbb != null && newbb != null && oldbb.intersects(newbb)) { // find a place for new items int dx = 0, dy = 0; int spacew = circuitArea.width - oldbb.width - newbb.width; int spaceh = circuitArea.height - oldbb.height - newbb.height; if (spacew > spaceh) dx = snapGrid(oldbb.x + oldbb.width - newbb.x + gridSize); else dy = snapGrid(oldbb.y + oldbb.height - newbb.y + gridSize); for (i = oldsz; i != elmList.size(); i++) { CircuitElm ce = getElm(i); ce.move(dx, dy); } // center circuit // handleResize(); } needAnalyze(); } void clearSelection() { int i; for (i = 0; i != elmList.size(); i++) { CircuitElm ce = getElm(i); ce.setSelected(false); } } void doSelectAll() { int i; for (i = 0; i != elmList.size(); i++) { CircuitElm ce = getElm(i); ce.setSelected(true); } } boolean anySelectedButMouse() { for (int i=0; i != elmList.size(); i++) if (getElm(i)!= mouseElm && getElm(i).selected) return true; return false; } // public void keyPressed(KeyEvent e) {} // public void keyReleased(KeyEvent e) {} boolean dialogIsShowing() { if (editDialog!=null && editDialog.isShowing()) return true; if (customLogicEditDialog!=null && customLogicEditDialog.isShowing()) return true; if (exportAsUrlDialog != null && exportAsUrlDialog.isShowing()) return true; if (exportAsTextDialog != null && exportAsTextDialog.isShowing()) return true; if (exportAsLocalFileDialog != null && exportAsLocalFileDialog.isShowing()) return true; if (contextPanel!=null && contextPanel.isShowing()) return true; if (scrollValuePopup != null && scrollValuePopup.isShowing()) return true; if (aboutBox !=null && aboutBox.isShowing()) return true; if (importFromTextDialog !=null && importFromTextDialog.isShowing()) return true; if (importFromDropboxDialog != null && importFromDropboxDialog.isShowing()) return true; return false; } public void onPreviewNativeEvent(NativePreviewEvent e) { int cc=e.getNativeEvent().getCharCode(); int t=e.getTypeInt(); int code=e.getNativeEvent().getKeyCode(); if (dialogIsShowing()) { if (scrollValuePopup != null && scrollValuePopup.isShowing() && (t & Event.ONKEYDOWN)!=0) { if (code==KEY_ESCAPE || code==KEY_SPACE) scrollValuePopup.close(false); if (code==KEY_ENTER) scrollValuePopup.close(true); } if (editDialog!=null && editDialog.isShowing() && (t & Event.ONKEYDOWN)!=0) { if (code==KEY_ESCAPE) editDialog.closeDialog(); if (code==KEY_ENTER) { editDialog.apply(); editDialog.closeDialog(); } } return; } if ((t & Event.ONKEYDOWN)!=0) { if (code==KEY_BACKSPACE || code==KEY_DELETE) { doDelete(); e.cancel(); } if (code==KEY_ESCAPE){ setMouseMode(MODE_SELECT); mouseModeStr = "Select"; tempMouseMode = mouseMode; e.cancel(); } if (e.getNativeEvent().getCtrlKey() || e.getNativeEvent().getMetaKey()) { if (code==KEY_C) { menuPerformed("key", "copy"); e.cancel(); } if (code==KEY_X) { menuPerformed("key", "cut"); e.cancel(); } if (code==KEY_V) { menuPerformed("key", "paste"); e.cancel(); } if (code==KEY_Z) { menuPerformed("key", "undo"); e.cancel(); } if (code==KEY_Y) { menuPerformed("key", "redo"); e.cancel(); } if (code==KEY_D) { menuPerformed("key", "duplicate"); e.cancel(); } if (code==KEY_A) { menuPerformed("key", "selectAll"); e.cancel(); } } } if ((t&Event.ONKEYPRESS)!=0) { if (cc=='-') { menuPerformed("key", "zoomout"); e.cancel(); } if (cc=='+' || cc == '=') { menuPerformed("key", "zoomin"); e.cancel(); } if (cc=='0') { menuPerformed("key", "zoom100"); e.cancel(); } if (cc>32 && cc<127){ String c=shortcuts[cc]; e.cancel(); if (c==null) return; setMouseMode(MODE_ADD_ELM); mouseModeStr=c; tempMouseMode = mouseMode; } if (cc==32) { setMouseMode(MODE_SELECT); mouseModeStr = "Select"; tempMouseMode = mouseMode; e.cancel(); } } } // factors a matrix into upper and lower triangular matrices by // gaussian elimination. On entry, a[0..n-1][0..n-1] is the // matrix to be factored. ipvt[] returns an integer vector of pivot // indices, used in the lu_solve() routine. boolean lu_factor(double a[][], int n, int ipvt[]) { double scaleFactors[]; int i,j,k; scaleFactors = new double[n]; // divide each row by its largest element, keeping track of the // scaling factors for (i = 0; i != n; i++) { double largest = 0; for (j = 0; j != n; j++) { double x = Math.abs(a[i][j]); if (x > largest) largest = x; } // if all zeros, it's a singular matrix if (largest == 0) return false; scaleFactors[i] = 1.0/largest; } // use Crout's method; loop through the columns for (j = 0; j != n; j++) { // calculate upper triangular elements for this column for (i = 0; i != j; i++) { double q = a[i][j]; for (k = 0; k != i; k++) q -= a[i][k]*a[k][j]; a[i][j] = q; } // calculate lower triangular elements for this column double largest = 0; int largestRow = -1; for (i = j; i != n; i++) { double q = a[i][j]; for (k = 0; k != j; k++) q -= a[i][k]*a[k][j]; a[i][j] = q; double x = Math.abs(q); if (x >= largest) { largest = x; largestRow = i; } } // pivoting if (j != largestRow) { double x; for (k = 0; k != n; k++) { x = a[largestRow][k]; a[largestRow][k] = a[j][k]; a[j][k] = x; } scaleFactors[largestRow] = scaleFactors[j]; } // keep track of row interchanges ipvt[j] = largestRow; // avoid zeros if (a[j][j] == 0.0) { System.out.println("avoided zero"); a[j][j]=1e-18; } if (j != n-1) { double mult = 1.0/a[j][j]; for (i = j+1; i != n; i++) a[i][j] *= mult; } } return true; } // Solves the set of n linear equations using a LU factorization // previously performed by lu_factor. On input, b[0..n-1] is the right // hand side of the equations, and on output, contains the solution. void lu_solve(double a[][], int n, int ipvt[], double b[]) { int i; // find first nonzero b element for (i = 0; i != n; i++) { int row = ipvt[i]; double swap = b[row]; b[row] = b[i]; b[i] = swap; if (swap != 0) break; } int bi = i++; for (; i < n; i++) { int row = ipvt[i]; int j; double tot = b[row]; b[row] = b[i]; // forward substitution using the lower triangular matrix for (j = bi; j < i; j++) tot -= a[i][j]*b[j]; b[i] = tot; } for (i = n-1; i >= 0; i--) { double tot = b[i]; // back-substitution using the upper triangular matrix int j; for (j = i+1; j != n; j++) tot -= a[i][j]*b[j]; b[i] = tot/a[i][i]; } } void createNewLoadFile() { // This is a hack to fix what IMHO is a bug in the <INPUT FILE element // reloading the same file doesn't create a change event so importing the same file twice // doesn't work unless you destroy the original input element and replace it with a new one int idx=verticalPanel.getWidgetIndex(loadFileInput); LoadFile newlf=new LoadFile(this); verticalPanel.insert(newlf, idx); verticalPanel.remove(idx+1); loadFileInput=newlf; } void addWidgetToVerticalPanel(Widget w) { if (iFrame!=null) { int i=verticalPanel.getWidgetIndex(iFrame); verticalPanel.insert(w, i); setiFrameHeight(); } else verticalPanel.add(w); } void removeWidgetFromVerticalPanel(Widget w){ verticalPanel.remove(w); if (iFrame!=null) setiFrameHeight(); } public static CircuitElm createCe(int tint, int x1, int y1, int x2, int y2, int f, StringTokenizer st) { if (tint=='g') return (CircuitElm) new GroundElm(x1, y1, x2, y2, f, st); if (tint=='r') return (CircuitElm) new ResistorElm(x1, y1, x2, y2, f, st); if (tint=='R') return (CircuitElm) new RailElm(x1, y1, x2, y2, f, st); if (tint=='s') return (CircuitElm) new SwitchElm(x1, y1, x2, y2, f, st); if (tint=='S') return (CircuitElm) new Switch2Elm(x1, y1, x2, y2, f, st); if (tint=='t') return (CircuitElm) new TransistorElm(x1, y1, x2, y2, f, st); if (tint=='w') return (CircuitElm) new WireElm(x1, y1, x2, y2, f, st); if (tint=='c') return (CircuitElm) new CapacitorElm(x1, y1, x2, y2, f, st); if (tint==209) return (CircuitElm) new PolarCapacitorElm(x1, y1, x2, y2, f, st); if (tint=='l') return (CircuitElm) new InductorElm(x1, y1, x2, y2, f, st); if (tint=='v') return (CircuitElm) new VoltageElm(x1, y1, x2, y2, f, st); if (tint==172) return (CircuitElm) new VarRailElm(x1, y1, x2, y2, f, st); if (tint==174) return (CircuitElm) new PotElm(x1, y1, x2, y2, f, st); if (tint=='O') return (CircuitElm) new OutputElm(x1, y1, x2, y2, f, st); if (tint=='i') return (CircuitElm) new CurrentElm(x1, y1, x2, y2, f, st); if (tint=='p') return (CircuitElm) new ProbeElm(x1, y1, x2, y2, f, st); if (tint=='d') return (CircuitElm) new DiodeElm(x1, y1, x2, y2, f, st); if (tint=='z') return (CircuitElm) new ZenerElm(x1, y1, x2, y2, f, st); if (tint==170) return (CircuitElm) new SweepElm(x1, y1, x2, y2, f, st); if (tint==162) return (CircuitElm) new LEDElm(x1, y1, x2, y2, f, st); if (tint=='A') return (CircuitElm) new AntennaElm(x1, y1, x2, y2, f, st); if (tint=='L') return (CircuitElm) new LogicInputElm(x1, y1, x2, y2, f, st); if (tint=='M') return (CircuitElm) new LogicOutputElm(x1, y1, x2, y2, f, st); if (tint=='T') return (CircuitElm) new TransformerElm(x1, y1, x2, y2, f, st); if (tint==169) return (CircuitElm) new TappedTransformerElm(x1, y1, x2, y2, f, st); if (tint==171) return (CircuitElm) new TransLineElm(x1, y1, x2, y2, f, st); if (tint==178) return (CircuitElm) new RelayElm(x1, y1, x2, y2, f, st); if (tint=='m') return (CircuitElm) new MemristorElm(x1, y1, x2, y2, f, st); if (tint==187) return (CircuitElm) new SparkGapElm(x1, y1, x2, y2, f, st); if (tint==200) return (CircuitElm) new AMElm(x1, y1, x2, y2, f, st); if (tint==201) return (CircuitElm) new FMElm(x1, y1, x2, y2, f, st); if (tint=='n') return (CircuitElm) new NoiseElm(x1, y1, x2, y2, f, st); if (tint==181) return (CircuitElm) new LampElm(x1, y1, x2, y2, f, st); if (tint=='a') return (CircuitElm) new OpAmpElm(x1, y1, x2, y2, f, st); if (tint=='f') return (CircuitElm) new MosfetElm(x1, y1, x2, y2, f, st); if (tint=='j') return (CircuitElm) new JfetElm(x1, y1, x2, y2, f, st); if (tint==159) return (CircuitElm) new AnalogSwitchElm(x1, y1, x2, y2, f, st); if (tint==160) return (CircuitElm) new AnalogSwitch2Elm(x1, y1, x2, y2, f, st); if (tint==180) return (CircuitElm) new TriStateElm(x1, y1, x2, y2, f, st); if (tint==182) return (CircuitElm) new SchmittElm(x1, y1, x2, y2, f, st); if (tint==183) return (CircuitElm) new InvertingSchmittElm(x1, y1, x2, y2, f, st); if (tint==177) return (CircuitElm) new SCRElm(x1, y1, x2, y2, f, st); if (tint==203) return (CircuitElm) new DiacElm(x1, y1, x2, y2, f, st); if (tint==206) return (CircuitElm) new TriacElm(x1, y1, x2, y2, f, st); if (tint==173) return (CircuitElm) new TriodeElm(x1, y1, x2, y2, f, st); if (tint==175) return (CircuitElm) new TunnelDiodeElm(x1, y1, x2, y2, f, st); if (tint==179) return (CircuitElm) new CC2Elm(x1, y1, x2, y2, f, st); if (tint=='I') return (CircuitElm) new InverterElm(x1, y1, x2, y2, f, st); if (tint==151) return (CircuitElm) new NandGateElm(x1, y1, x2, y2, f, st); if (tint==153) return (CircuitElm) new NorGateElm(x1, y1, x2, y2, f, st); if (tint==150) return (CircuitElm) new AndGateElm(x1, y1, x2, y2, f, st); if (tint==152) return (CircuitElm) new OrGateElm(x1, y1, x2, y2, f, st); if (tint==154) return (CircuitElm) new XorGateElm(x1, y1, x2, y2, f, st); if (tint==155) return (CircuitElm) new DFlipFlopElm(x1, y1, x2, y2, f, st); if (tint==156) return (CircuitElm) new JKFlipFlopElm(x1, y1, x2, y2, f, st); if (tint==157) return (CircuitElm) new SevenSegElm(x1, y1, x2, y2, f, st); if (tint==184) return (CircuitElm) new MultiplexerElm(x1, y1, x2, y2, f, st); if (tint==185) return (CircuitElm) new DeMultiplexerElm(x1, y1, x2, y2, f, st); if (tint==189) return (CircuitElm) new SipoShiftElm(x1, y1, x2, y2, f, st); if (tint==186) return (CircuitElm) new PisoShiftElm(x1, y1, x2, y2, f, st); if (tint==161) return (CircuitElm) new PhaseCompElm(x1, y1, x2, y2, f, st); if (tint==164) return (CircuitElm) new CounterElm(x1, y1, x2, y2, f, st); if (tint==163) return (CircuitElm) new DecadeElm(x1, y1, x2, y2, f, st); if (tint==165) return (CircuitElm) new TimerElm(x1, y1, x2, y2, f, st); if (tint==166) return (CircuitElm) new DACElm(x1, y1, x2, y2, f, st); if (tint==167) return (CircuitElm) new ADCElm(x1, y1, x2, y2, f, st); if (tint==168) return (CircuitElm) new LatchElm(x1, y1, x2, y2, f, st); if (tint==188) return (CircuitElm) new SeqGenElm(x1, y1, x2, y2, f, st); if (tint==158) return (CircuitElm) new VCOElm(x1, y1, x2, y2, f, st); if (tint=='b') return (CircuitElm) new BoxElm(x1, y1, x2, y2, f, st); if (tint=='x') return (CircuitElm) new TextElm(x1, y1, x2, y2, f, st); if (tint==193) return (CircuitElm) new TFlipFlopElm(x1, y1, x2, y2, f, st); if (tint==197) return (CircuitElm) new SevenSegDecoderElm(x1, y1, x2, y2, f, st); if (tint==196) return (CircuitElm) new FullAdderElm(x1, y1, x2, y2, f, st); if (tint==195) return (CircuitElm) new HalfAdderElm(x1, y1, x2, y2, f, st); if (tint==194) return (CircuitElm) new MonostableElm(x1, y1, x2, y2, f, st); if (tint==207) return (CircuitElm) new LabeledNodeElm(x1, y1, x2, y2, f, st); if (tint==208) return (CircuitElm) new CustomLogicElm(x1, y1, x2, y2, f, st); if (tint==210) return (CircuitElm) new DataRecorderElm(x1, y1, x2, y2, f, st); if (tint==211) return (CircuitElm) new AudioOutputElm(x1, y1, x2, y2, f, st); if (tint==212) return (CircuitElm) new VCVSElm(x1, y1, x2, y2, f, st); if (tint==213) return (CircuitElm) new VCCSElm(x1, y1, x2, y2, f, st); if (tint==214) return (CircuitElm) new CCVSElm(x1, y1, x2, y2, f, st); if (tint==215) return (CircuitElm) new CCCSElm(x1, y1, x2, y2, f, st); if (tint==368) return new TestPointElm(x1, y1, x2, y2, f, st); if (tint==370) return new AmmeterElm(x1, y1, x2, y2, f, st); if (tint==400) return new DarlingtonElm(x1, y1, x2, y2, f, st); if (tint==401) return new ComparatorElm(x1, y1, x2, y2, f, st); if (tint==402) return new OTAElm(x1, y1, x2, y2, f, st); return null; } public static CircuitElm constructElement(String n, int x1, int y1){ if (n=="GroundElm") return (CircuitElm) new GroundElm(x1, y1); if (n=="ResistorElm") return (CircuitElm) new ResistorElm(x1, y1); if (n=="RailElm") return (CircuitElm) new RailElm(x1, y1); if (n=="SwitchElm") return (CircuitElm) new SwitchElm(x1, y1); if (n=="Switch2Elm") return (CircuitElm) new Switch2Elm(x1, y1); if (n=="NTransistorElm") return (CircuitElm) new NTransistorElm(x1, y1); if (n=="PTransistorElm") return (CircuitElm) new PTransistorElm(x1, y1); if (n=="WireElm") return (CircuitElm) new WireElm(x1, y1); if (n=="CapacitorElm") return (CircuitElm) new CapacitorElm(x1, y1); if (n=="PolarCapacitorElm") return (CircuitElm) new PolarCapacitorElm(x1, y1); if (n=="InductorElm") return (CircuitElm) new InductorElm(x1, y1); if (n=="DCVoltageElm") return (CircuitElm) new DCVoltageElm(x1, y1); if (n=="VarRailElm") return (CircuitElm) new VarRailElm(x1, y1); if (n=="PotElm") return (CircuitElm) new PotElm(x1, y1); if (n=="OutputElm") return (CircuitElm) new OutputElm(x1, y1); if (n=="CurrentElm") return (CircuitElm) new CurrentElm(x1, y1); if (n=="ProbeElm") return (CircuitElm) new ProbeElm(x1, y1); if (n=="DiodeElm") return (CircuitElm) new DiodeElm(x1, y1); if (n=="ZenerElm") return (CircuitElm) new ZenerElm(x1, y1); if (n=="ACVoltageElm") return (CircuitElm) new ACVoltageElm(x1, y1); if (n=="ACRailElm") return (CircuitElm) new ACRailElm(x1, y1); if (n=="SquareRailElm") return (CircuitElm) new SquareRailElm(x1, y1); if (n=="SweepElm") return (CircuitElm) new SweepElm(x1, y1); if (n=="LEDElm") return (CircuitElm) new LEDElm(x1, y1); if (n=="AntennaElm") return (CircuitElm) new AntennaElm(x1, y1); if (n=="LogicInputElm") return (CircuitElm) new LogicInputElm(x1, y1); if (n=="LogicOutputElm") return (CircuitElm) new LogicOutputElm(x1, y1); if (n=="TransformerElm") return (CircuitElm) new TransformerElm(x1, y1); if (n=="TappedTransformerElm") return (CircuitElm) new TappedTransformerElm(x1, y1); if (n=="TransLineElm") return (CircuitElm) new TransLineElm(x1, y1); if (n=="RelayElm") return (CircuitElm) new RelayElm(x1, y1); if (n=="MemristorElm") return (CircuitElm) new MemristorElm(x1, y1); if (n=="SparkGapElm") return (CircuitElm) new SparkGapElm(x1, y1); if (n=="ClockElm") return (CircuitElm) new ClockElm(x1, y1); if (n=="AMElm") return (CircuitElm) new AMElm(x1, y1); if (n=="FMElm") return (CircuitElm) new FMElm(x1, y1); if (n=="LampElm") return (CircuitElm) new LampElm(x1, y1); if (n=="PushSwitchElm") return (CircuitElm) new PushSwitchElm(x1, y1); if (n=="OpAmpElm") return (CircuitElm) new OpAmpElm(x1, y1); if (n=="OpAmpSwapElm") return (CircuitElm) new OpAmpSwapElm(x1, y1); if (n=="NMosfetElm") return (CircuitElm) new NMosfetElm(x1, y1); if (n=="PMosfetElm") return (CircuitElm) new PMosfetElm(x1, y1); if (n=="NJfetElm") return (CircuitElm) new NJfetElm(x1, y1); if (n=="PJfetElm") return (CircuitElm) new PJfetElm(x1, y1); if (n=="AnalogSwitchElm") return (CircuitElm) new AnalogSwitchElm(x1, y1); if (n=="AnalogSwitch2Elm") return (CircuitElm) new AnalogSwitch2Elm(x1, y1); if (n=="SchmittElm") return (CircuitElm) new SchmittElm(x1, y1); if (n=="InvertingSchmittElm") return (CircuitElm) new InvertingSchmittElm(x1, y1); if (n=="TriStateElm") return (CircuitElm) new TriStateElm(x1, y1); if (n=="SCRElm") return (CircuitElm) new SCRElm(x1, y1); if (n=="DiacElm") return (CircuitElm) new DiacElm(x1, y1); if (n=="TriacElm") return (CircuitElm) new TriacElm(x1, y1); if (n=="TriodeElm") return (CircuitElm) new TriodeElm(x1, y1); if (n=="TunnelDiodeElm") return (CircuitElm) new TunnelDiodeElm(x1, y1); if (n=="CC2Elm") return (CircuitElm) new CC2Elm(x1, y1); if (n=="CC2NegElm") return (CircuitElm) new CC2NegElm(x1, y1); if (n=="InverterElm") return (CircuitElm) new InverterElm(x1, y1); if (n=="NandGateElm") return (CircuitElm) new NandGateElm(x1, y1); if (n=="NorGateElm") return (CircuitElm) new NorGateElm(x1, y1); if (n=="AndGateElm") return (CircuitElm) new AndGateElm(x1, y1); if (n=="OrGateElm") return (CircuitElm) new OrGateElm(x1, y1); if (n=="XorGateElm") return (CircuitElm) new XorGateElm(x1, y1); if (n=="DFlipFlopElm") return (CircuitElm) new DFlipFlopElm(x1, y1); if (n=="JKFlipFlopElm") return (CircuitElm) new JKFlipFlopElm(x1, y1); if (n=="SevenSegElm") return (CircuitElm) new SevenSegElm(x1, y1); if (n=="MultiplexerElm") return (CircuitElm) new MultiplexerElm(x1, y1); if (n=="DeMultiplexerElm") return (CircuitElm) new DeMultiplexerElm(x1, y1); if (n=="SipoShiftElm") return (CircuitElm) new SipoShiftElm(x1, y1); if (n=="PisoShiftElm") return (CircuitElm) new PisoShiftElm(x1, y1); if (n=="PhaseCompElm") return (CircuitElm) new PhaseCompElm(x1, y1); if (n=="CounterElm") return (CircuitElm) new CounterElm(x1, y1); if (n=="DecadeElm") return (CircuitElm) new DecadeElm(x1, y1); if (n=="TimerElm") return (CircuitElm) new TimerElm(x1, y1); if (n=="DACElm") return (CircuitElm) new DACElm(x1, y1); if (n=="ADCElm") return (CircuitElm) new ADCElm(x1, y1); if (n=="LatchElm") return (CircuitElm) new LatchElm(x1, y1); if (n=="SeqGenElm") return (CircuitElm) new SeqGenElm(x1, y1); if (n=="VCOElm") return (CircuitElm) new VCOElm(x1, y1); if (n=="BoxElm") return (CircuitElm) new BoxElm(x1, y1); if (n=="TextElm") return (CircuitElm) new TextElm(x1, y1); if (n=="TFlipFlopElm") return (CircuitElm) new TFlipFlopElm(x1, y1); if (n=="SevenSegDecoderElm") return (CircuitElm) new SevenSegDecoderElm(x1, y1); if (n=="FullAdderElm") return (CircuitElm) new FullAdderElm(x1, y1); if (n=="HalfAdderElm") return (CircuitElm) new HalfAdderElm(x1, y1); if (n=="MonostableElm") return (CircuitElm) new MonostableElm(x1, y1); if (n=="LabeledNodeElm") return (CircuitElm) new LabeledNodeElm(x1, y1); if (n=="UserDefinedLogicElm") return (CircuitElm) new CustomLogicElm(x1, y1); if (n=="TestPointElm") return new TestPointElm(x1, y1); if (n=="AmmeterElm") return new AmmeterElm(x1, y1); if (n=="DataRecorderElm") return (CircuitElm) new DataRecorderElm(x1, y1); if (n=="AudioOutputElm") return (CircuitElm) new AudioOutputElm(x1, y1); if (n=="NDarlingtonElm") return (CircuitElm) new NDarlingtonElm(x1, y1); if (n=="PDarlingtonElm") return (CircuitElm) new PDarlingtonElm(x1, y1); if (n=="ComparatorElm") return (CircuitElm) new ComparatorElm(x1, y1); if (n=="OTAElm") return (CircuitElm) new OTAElm(x1, y1); if (n=="NoiseElm") return (CircuitElm) new NoiseElm(x1, y1); if (n=="VCVSElm") return (CircuitElm) new VCVSElm(x1, y1); if (n=="VCCSElm") return (CircuitElm) new VCCSElm(x1, y1); if (n=="CCVSElm") return (CircuitElm) new CCVSElm(x1, y1); if (n=="CCCSElm") return (CircuitElm) new CCCSElm(x1, y1); return null; } public void updateModels() { int i; for (i = 0; i != elmList.size(); i++) elmList.get(i).updateModels(); } native boolean weAreInUS() /*-{ try { l = navigator.languages ? navigator.languages[0] : (navigator.language || navigator.userLanguage) ; if (l.length > 2) { l = l.slice(-2).toUpperCase(); return (l == "US" || l=="CA"); } else { return 0; } } catch (e) { return 0; } }-*/; static String LS(String s) { String sm = localizationMap.get(s); return sm != null ? sm : s; } static SafeHtml LSHTML(String s) { return SafeHtmlUtils.fromTrustedString(LS(s)); } // For debugging void dumpNodelist() { CircuitNode nd; CircuitElm e; int i,j; String s; String cs; // // for(i=0; i<nodeList.size(); i++) { // s="Node "+i; // nd=nodeList.get(i); // for(j=0; j<nd.links.size();j++) { // s=s+" " + nd.links.get(j).num + " " +nd.links.get(j).elm.getDumpType(); // } // console(s); // } console("Elm list Dump"); for (i=0;i<elmList.size(); i++) { e=elmList.get(i); cs = e.getDumpClass().toString(); int p = cs.lastIndexOf('.'); cs = cs.substring(p+1); if (cs=="WireElm") continue; if (cs=="LabeledNodeElm") cs = cs+" "+((LabeledNodeElm)e).text; if (cs=="TransistorElm") { if (((TransistorElm)e).pnp == -1) cs= "PTransistorElm"; else cs = "NTransistorElm"; } s=cs; for(j=0; j<e.getPostCount(); j++) { s=s+" "+e.nodes[j]; } console(s); } } }